guys. I am learning glsl for WebGL application. I have problem about choosing color type in glsl.
It seems I can use both vec3 and vec4 as color. After researching, I found THREE.js use vec3:
// from https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl
struct BlinnPhongMaterial {
vec3 diffuseColor;
vec3 specularColor;
float specularShininess;
float specularStrength;
};
But in Babylon.js, osgjs, playcanvas, all of them use vec4.
What's the difference between using vec3 and vec4 for color type ?
Which should I choose ?
Thanks.
I would like to apply multiples lights in my shader. To do that, I've make the choice to put lights data in multiple arrays instead of one light = one struct to avoid doing "setUniforms" a lot of time.
I've got something like that:
// Light's data.
struct LightData
{
vec3 ambient[4];
vec3 diffuse[4];
vec3 position[4];
vec3 specular[4];
};
uniform LightData lights;
vec3 applyLight( int index, vec3 cameraPos )
{
vec3 position = lights.position[index]; //<<< Error here.
…
return result;
}
int main()
{
vec3 color;
for( int i = 0; i < 4; i++)
color += applyLight(i, camera.position);
}
The problem is in the GLSL code: "index expression must be constant []". How can I do to have this code to work? I could give position, diffuse, ambient, specular to the function but it will by dirty.
What about performances? It is better to have something like what I've done or like the code bellow?
struct Light
{
vec3 ambient;
vec3 diffuse;
vec3 position;
vec3 specular;
};
uniform Light lights[4];
Have a nice day!
Unroll your loop:
color += applyLight(0, camera.position);
color += applyLight(1, camera.position);
color += applyLight(2, camera.position);
color += applyLight(3, camera.position);
(not sure if this exact code will work, but you get the idea)
lights.position[index]; // no
lights.position[0]; // yes, etc
Option below (for uniform layout) might be better, it may have a better locality.
Could someone assist me or head me in the right direction to implement the basic FVFs from DirectX in GLSL code? I completely understand how to create a program, apply VBOs and all that, but I'm having great difficulty in the actual creation of the shaders. Namely:
transformed+lit (x,y,color,specular,tu,tv)
lit (x,y,z,color,specular,tu,tv)
unlit (x,y,z,nx,ny,nz,tu,tv) [material/lights]
With this, I'd be given enough to implement far more interesting shaders.
So, I'm not asking for a mechanism to deal with FVFs. I'm simply asking, for the shader code, given the proper streams. I understand that the unlit and lit versions rely on passing in matrices and I completely understand the concept. I am just having trouble finding shader examples showing these concepts.
Okay. If you have troubles finding working shaders, there is example (Honestly, you can find it at any OpenGL book).
This shader program will use your object's world matrix and camera's matrices to transform vertices, and then map one texture to pixels and lit them with one directional light, (according to material properties and light direction).
Vertex shader:
#version 330
// Vertex input layout
attribute vec3 inPosition;
attribute vec3 inNormal;
attribute vec4 inVertexCol;
attribute vec2 inTexcoord;
attribute vec3 inTangent;
attribute vec3 inBitangent;
// Output
struct PSIn
{
vec3 normal;
vec4 vertexColor;
vec2 texcoord;
vec3 tangent;
vec3 bitangent;
};
out PSIn psin;
// Uniform buffers
layout(std140)
uniform CameraBuffer
{
mat4 mtxView;
mat4 mtxProj;
vec3 cameraPosition;
};
layout(std140)
uniform ObjectBuffer
{
mat4 mtxWorld;
};
void main()
{
// transform position
vec4 pos = vec4(inPosition, 1.0f);
pos = mtxWorld * pos;
pos = mtxView * pos;
pos = mtxProj * pos;
gl_Position = pos;
// just pass-through other stuff
psin.normal = inNormal;
psin.tangent = inTangent;
psin.bitangent = inBitangent;
psin.texcoord = inTexcoord;
psin.vertexColor = inVertexCol;
}
And fragment shader:
#version 330
// Input
in vec3 position;
in vec3 normal;
in vec4 vertexColor;
in vec2 texcoord;
in vec3 tangent;
in vec3 bitangent;
// Output
out vec4 fragColor;
// Uniforms
uniform sampler2D sampler0;
layout(std140)
uniform CameraBuffer
{
mat4 mtxView;
mat4 mtxProj;
vec3 cameraPosition;
};
layout(std140)
uniform ObjectBuffer
{
mat4 mtxWorld;
};
layout(std140)
uniform LightBuffer
{
vec3 lightDirection;
};
struct Material
{
float Ka; // ambient quotient
float Kd; // diffuse quotient
float Ks; // specular quotient
float A; // shininess
};
layout(std140)
uniform MaterialBuffer
{
Material material;
};
// function to calculate pixel lighting
float Lit( Material material, vec3 pos, vec3 nor, vec3 lit, vec3 eye )
{
vec3 V = normalize( eye - pos );
vec3 R = reflect( lit, nor);
float Ia = material.Ka;
float Id = material.Kd * clamp( dot(nor, -lit), 0.0f, 1.0f );
float Is = material.Ks * pow( clamp(dot(R,V), 0.0f, 1.0f), material.A );
return Ia + Id + Is;
}
void main()
{
vec3 nnormal = normalize(normal);
vec3 ntangent = normalize(tangent);
vec3 nbitangent = normalize(bitangent);
vec4 outColor = texture(sampler0, texcoord); // texture mapping
outColor *= Lit( material, position, nnormal, lightDirection, cameraPosition ); // lighting
outColor.w = 1.0f;
fragColor = outColor;
}
If you don't want texturing, just don't sample texture, but equate outColor to vertexColor.
If you don't need lighting, just comment out Lit() function.
Edit:
For 2D objects you can still use same program, but many of functionality will be redundant. You can strip out:
camera
light
material
all of vertex attributes, but inPosition and inTexcoord (maybe also inVertexCol, f you need vertices to have color) and all of code related with unneeded attributes
inPosition can be vec2
you will need to pass orthographic projection matrix instead of perspective one
you can even strip out matrices, and pass vertex buffer with positions in pixels. See my answer here about how to transform those pixel positions to screen space positions. You can do it either in C/C++ code or in GLSL/HLSL.
Hope it helps somehow.
Intro
You've not specified OpenGL/GLSL version that you targeting, so I'll assume that it is at least OpenGL 3.
One of the main advantages of programmable pipeline, to be compared with with fixed-function pipeline, is fully customizable vertex input. I'm not quite sure, if it is a good idea to introduce such constraints as fixed vertex format. For what?.. (You will find modern approach in paragraph "Another way" of my post)
But, if you really want to emulate fixed-function...
I think you'll need to have a vertex shader for each vertex format
you have, or somehow generate vertex shader on the fly. Or even for
all of the shader stages.
For example, for x, y, color, tu, tv input you will have vertex
shader such as:
attribute vec2 inPosition;
attribute vec4 inCol;
attribute vec2 inTexcoord;
void main()
{
...
}
As you don't have transforms, light and materials fixed-functionality in OpenGL 3, you must implement it yourself:
You must pass matrices for transformations
For lit shader you must pass additional variables, such as light direction
For material shader you must have materials in input
Typically, in shader, you do it with uniforms or uniform blocks:
layout(std140)
uniform CameraBuffer
{
mat4 mtxView;
mat4 mtxProj;
vec3 cameraPosition;
};
layout(std140)
uniform ObjectBuffer
{
mat4 mtxWorld;
};
layout(std140)
uniform LightBuffer
{
vec3 lightDirection;
};
struct Material
{
float Ka;
float Kd;
float Ks;
float A;
};
layout(std140)
uniform MaterialBuffer
{
Material material;
};
Probably, you can somehow combine all of shaders with different formats , uniforms, etc. in one big ubershader with branching.
Another way
You can stick to modern approach and just allow user to declare vertex format he wants (format, that he used in his shader). Just implement concept similar to IDirect3DDevice9::CreateVertexDeclaration or ID3D11Device::CreateInputLayout: you will make use of glVertexAttribPointer() and, probably, VAOs. This way you can also abstract out vertex layout, in API-independent way.
The main ideas are:
user passes an array of structures that describes format in API-independent way to your function (this struct can be similar to D3DVERTEXELEMENT9 or D3D11_INPUT_ELEMENT_DESC)
that function interpret array's elements one by one and builds some kind of internal info that describes format in API-specific way (such as IDirect3DVertexDeclaration9 for D3D9, ID3D11InputLayout for D3D11 or custom struct or VAO for OpenGL)
when it's time to set vertex format you just use this info
P.S. If you need ideas on how to properly implement light, materials in GLSL (I mean algorithms here), you'd better pick up some book or online tutorials, than asking here. Or just Google up "GLSL lighting".
You can find interesting these links:
Good resources for learning modern OpenGL (3.0 or later)?
OpenGL documentation
Select Books on OpenGL and 3D Graphics Coding
Happy coding!
I have a point light in my scene. I thought it worked correctly until I tested it with the camera looking at the lit object from different angles and found that the light area moves on the mesh (in my case simple plane). I'm using a typical ADS Phong lighting approach. I transform light position into camera space on the client side and then transform the interpolated vertex in the vertex shader with model view matrix.
My vertex shader looks like this:
#version 420
layout(location = 0) in vec4 position;
layout(location = 1) in vec2 uvs;
layout(location = 2) in vec3 normal;
uniform mat4 MVP_MATRIX;
uniform mat4 MODEL_VIEW_MATRIX;
uniform mat4 VIEW_MATRIX;
uniform mat3 NORMAL_MATRIX;
uniform vec4 DIFFUSE_COLOR;
//======= OUTS ============//
out smooth vec2 uvsOut;
out flat vec4 diffuseOut;
out vec3 Position;
out smooth vec3 Normal;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
uvsOut = uvs;
diffuseOut = DIFFUSE_COLOR;
Normal = normal;
Position = vec3(MODEL_VIEW_MATRIX * position);
gl_Position = MVP_MATRIX * position;
}
The fragment shader :
//==================== Uniforms ===============================
struct LightInfo{
vec4 Lp;///light position
vec3 Li;///light intensity
vec3 Lc;///light color
int Lt;///light type
};
const int MAX_LIGHTS=5;
uniform LightInfo lights[1];
// material props:
uniform vec3 KD;
uniform vec3 KA;
uniform vec3 KS;
uniform float SHININESS;
uniform int num_lights;
////ADS lighting method :
vec3 pointlightType( int lightIndex,vec3 position , vec3 normal) {
vec3 n = normalize(normal);
vec4 lMVPos = lights[0].Lp ; //
vec3 s = normalize(vec3(lMVPos.xyz) - position); //surf to light
vec3 v = normalize(vec3(-position)); //
vec3 r = normalize(- reflect(s , n));
vec3 h = normalize(v+s);
float sDotN = max( 0.0 , dot(s, n) );
vec3 diff = KD * lights[0].Lc * sDotN ;
diff = clamp(diff ,0.0 ,1.0);
vec3 spec = vec3(0,0,0);
if (sDotN > 0.0) {
spec = KS * pow( max( 0.0 ,dot(n,h) ) , SHININESS);
spec = clamp(spec ,0.0 ,1.0);
}
return lights[0].Li * ( spec+diff);
}
I have studied a lot of tutorials but none of those gives thorough explanation on the whole process when it comes to transform spaces.I suspect it has something to do with camera space I transform light and vertex position into.In my case the view matrix is created with
glm::lookAt()
which always negates "eye" vector so it comes that the view matrix in my shaders has negated translation part.Is is supposed to be like that? Can someone give a detailed explanation how it is done the right way in programmable pipeline? My shaders are implemented based on the book "OpenGL 4.0 Shading language cookbook" .The author seems to use also the camera space.But it doesn't work right unless that is the way it should work ...
I just moved the calculations into the world space.Now the point light stays on the spot.But how do I achieve the same using camera space?
I nailed down the bug and it was pretty stupid one.But it maybe helpful to others who are too much "math friendly" .My light position in the shaders is defined with vec3 .Now , on the client side it is represented with vec4.I was effectively setting .w component of the vec4 to be equal zero each time before transforming it with view matrix.Doing so ,I believe , the light position vector wasn't getting transformed correctly and from this all the light position problems stems in the shader.The solution is to keep w component of light position vector to be always equal 1.
I have a fragment shader, it has structs and a uniform of those structs. When I tried to compile them, OpenGL gave me this error:
0(30) : error C0000: syntax error, unexpected identifier, expecting '{' at token "lights_a"
0(31) : error C0000: syntax error, unexpected identifier, expecting '{' at token "material"
I don't know what the problem is here. I have been searching for the problem... I looked at line 30 and 31, I did everything I could imagine, but without success.
Here is the code:
#version 330 core
struct LightBase
{
int renderit;
vec4 ambient_light;
vec4 specular_light;
vec4 diffuse_light;
float radius;
vec3 light_position;
vec3 light_direction;
int light_type;
};
struct MaterialBase
{
vec4 ambient_affect;
vec4 specular_affect;
vec4 diffuse_affect;
float shining;
float mirror;
int light_affect;
};
in vec3 VertexPos;
in vec3 Normal;
uniform int light_quantity;
uniform LightBase lights_a[50];
uniform MaterialBase material;
uniform float usingTex;
uniform sampler2D texturemap;
in vec2 UVs;
in vec4 Colors;
out vec4 color;
void main() {
vec4 texture_u = texture(texturemap,UVs).rgba * usingTex;
vec4 color_u = Colors * (1.0f-usingTex);
vec4 final_color = color_u+texture_u;
// Light
for(int i=0;i<light_quantity;i++) {
if(lights_a[i].renderit==1) {
if(lights_a[i].light_type==1) {
float attenuation = max(0.0,1.0-dot(lights_a[i].light_direction,lights_a[i].light_direction));
vec3 L = normalize(lights_a[i].light_direction);
vec3 N = normalize(Normal);
vec3 V = normalize(-VertexPos);
vec3 R = normalize(-reflect(L,N));
float nDotL = max(0.0,dot(N,L));
float rDotV = max(0.0,dot(R,V));
float ambient_result = lights_a[i].ambient_light * material.ambient_affect * attenuation;
float diffuse_result = lights_a[i].diffuse_light * material.diffuse_affect * nDotL * attenuation;
float specular_result = lights_a[i].specular_light * material.specular_affect * pow(rDotV,material.shining) * attenuation;
vec4 this_colour = (ambient_result + diffuse_result + specular_result) * final_color;
final_color = this_colour;
}
}
}
color = final_color;
}
What's wrong about the code?
I don't see an error when I compile your code -- only a couple of warnings:
0(62) : error C7011: implicit cast from "vec4" to "float"
0(63) : error C7011: implicit cast from "vec4" to "float"
0(64) : error C7011: implicit cast from "vec4" to "float"
I can almost reproduce the error you see by commenting out the declarations of struct LightBase and struct MaterialBase -- except they show up on lines 33 and 34. which are the lines with the noted tokens lights_a and material
This leads me to believe that your problem is that you're not actually compiling the program you think you are. Perhaps you're reading it from a file into memory, but the memory gets corrupted somehow before you call glShaderSource...