S
Shifty
Guest
I've been experimenting with lighting using shaders and normal maps. I eventually want to have a scene where i have multiple lights reacting with objects and their normal maps. However I am unsure how to efficiently get multiple normal/diffuse maps into the shader.
Heres a picture of what my current test scene looks like. Its just a brick wall diffuse map w/ corresponding normals map.
Here is my vertex and fragment shaders
And my light controller object
Now earlier i said efficiently because I have another object in the scene, a simple "player" triangle with a normal map. I can get the light to work properly with the player object as I want but i have to do the same fetching of uniforms in the create event, then set the shader in the draw event. I feel like having to do a "set uniforms and set shader" script call for every object i want interacting with this light source is going to become extraordinarily inefficient.
So how can i dynamically pass multiple sampler2D textures into this lighting shader at once?
Addon question: is there a GMS manual entry that covers some of the GM specific shader functions/built-in vars i.e: "noise1()", far, and the long list of "gl_" vars?
Heres a picture of what my current test scene looks like. Its just a brick wall diffuse map w/ corresponding normals map.
Here is my vertex and fragment shaders
Code:
attribute vec3 in_Position; // (x,y,z)
attribute vec4 in_Colour; // (r,g,b,a)
attribute vec2 in_TextureCoord; // (u,v)
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
varying vec2 Fragcoord;
void main()
{
vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
Fragcoord = in_Position.xy;
v_vColour = in_Colour;
v_vTexcoord = in_TextureCoord;
}
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
varying vec2 Fragcoord;
//texture samplers
uniform sampler2D u_texture; //diffuse map
uniform sampler2D u_normals; //normal map
//shader algorithm values
uniform vec2 Resolution; //screen resolution
uniform vec3 LightPos; //light postion, normalized
uniform vec4 LightColor; //light RGBA -- alpha is intensity
uniform vec4 AmbientColor; //Ambient RGBA -- alpha is intenisty
uniform vec3 Falloff; //attenuation coefficients
void main()
{
//vec3 LightPos = vec3(0.1, 0.8, 0.5);
//RGBA of diffuse color
vec4 DiffuseColor = texture2D(u_texture, v_vTexcoord);
//RGB of normal map
vec3 NormalMap = texture2D(u_normals, v_vTexcoord).rgb;
NormalMap.g = 1.0 - NormalMap.g;
//delta position of light
//res and light poss need to be passed in
vec3 LightDir = vec3(LightPos.xy - (gl_FragCoord.xy / Resolution.xy), LightPos.z);
//Correct for aspect ratio
LightDir.x *= Resolution.x / Resolution.y;
//Determin distance (for attenuation) BEFORE normalizing LightDir vector
float D = length(LightDir);
//Normalize vectors
vec3 N = normalize(NormalMap * 2.0 - 1.0);
vec3 L = normalize(LightDir);
//Pre-multiply light color with intensity
//then perform "N dot L to determine our diffuse term
vec3 Diffuse = (LightColor.rgb * LightColor.a) * max(dot(N , L), 0.0);
//pre-multiply ambient color with intensity
vec3 Ambient = AmbientColor.rgb* AmbientColor.a;
//Calculate attenuation
float Attenuation = 1.0 / (Falloff.x + (Falloff.y*D) + (Falloff.z*D*D) );
//Final color calculation
vec3 Intensity = Ambient + Diffuse * Attenuation;
vec3 FinalColor = DiffuseColor.rgb * Intensity;
gl_FragColor = v_vColour * vec4(FinalColor, DiffuseColor.a);
}
And my light controller object
Code:
LIGHT_COLOR = [1.0, 0.8, 0.6, 1.0];
AMBIENT_COLOR = [0.7, 0.7, 1.0, 0.2];
FALLOFF = [0.5, 0.5, 10.0];
//shader uniforms
res = shader_get_uniform(normal_map_lighting, "Resolution");
lightP = shader_get_uniform(normal_map_lighting, "LightPos");
lightC = shader_get_uniform(normal_map_lighting, "LightColor");
ambientC = shader_get_uniform(normal_map_lighting, "AmbientColor");
falloff = shader_get_uniform(normal_map_lighting, "Falloff");
//shader sampler textures
diffuseSampler = shader_get_sampler_index(normal_map_lighting, "u_texture");
normalSampler = shader_get_sampler_index(normal_map_lighting, "u_normals");
spr_diffuse = sprite_get_texture(bg, 0);
spr_normals = sprite_get_texture(bg_n, 0);
//create surface
shader_surf = -1;
Code:
var RESOLUTION = [window_get_width(), window_get_height()];
var LIGHT_POS = [mx, my, 0.06]; //mx , my are just normalized mouse coords
shader_set(normal_map_lighting);
//set shader algorithm values
shader_set_uniform_f_array(res, RESOLUTION);
shader_set_uniform_f_array(lightP, LIGHT_POS);
shader_set_uniform_f_array(lightC, LIGHT_COLOR);
shader_set_uniform_f_array(ambientC, AMBIENT_COLOR);
shader_set_uniform_f_array(falloff, FALLOFF);
//set shader texture samplers
texture_set_stage(diffuseSampler, spr_diffuse);
texture_set_stage(normalSampler, spr_normals);
if !surface_exists(shader_surf) {
shader_surf = surface_create(room_width, room_height);
} else {
if view_current = 0 {
draw_surface(shader_surf, 0, 0);
}
}
shader_reset();
Now earlier i said efficiently because I have another object in the scene, a simple "player" triangle with a normal map. I can get the light to work properly with the player object as I want but i have to do the same fetching of uniforms in the create event, then set the shader in the draw event. I feel like having to do a "set uniforms and set shader" script call for every object i want interacting with this light source is going to become extraordinarily inefficient.
So how can i dynamically pass multiple sampler2D textures into this lighting shader at once?
Addon question: is there a GMS manual entry that covers some of the GM specific shader functions/built-in vars i.e: "noise1()", far, and the long list of "gl_" vars?