How to pass a custom struct from vertex shader to fragment shader - opengl

In the fragment shader, I defined two structures as follows
struct DirLight{
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
struct PointLight {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
and in vertex shader, I defined the following variables, because I first want to do some transformations (like matrix multiplication that is not recommended in the fragment shader) on these uniform variables in the vertex shader.
uniform DirLight dirLight; // only one directional light
uniform int pointLightCnt; // number of point light sources
uniform PointLight pointLight[MAX]; // point lights
What should I do to transfer the structure in the vertex shader to the fragment shader?
Can I use a method similar to c++ like:
Define the structure in the header file, include them in both the vertex shader and the fragment shader, then define the corresponding out variable in the vertex shader, and define the corresponding in variable in the fragment shader to achieve it?

I was going to go into a long explanation of how to implement your lighting structure so it is generic to any light type, but that is a separate issue.
Your current issue is that the Vertex Function shouldn't need to use the lighting uniform at all; there's no data to pass between them. The only thing the Vertex shader should be doing is converting the local space to clip space and saving the intermediate world space as a separate part of the fragment shader's input so it can calculate the lighting properly.
All the lighting calculations can be done on the pixel/fragment shader and any dynamic lighting (positions, penumbra calculations, direction changes, etc) should be done on the CPU and just passed on to the GPU in the lighting buffer/uniform all at once.
This is in hlsl, but it's easily converted to glsl:
//Uniform
cbuffer matrix_cb : register(b0) {
float4x4 g_MODEL;
float4x4 g_VIEW;
float4x4 g_PROJECTION;
};
struct vs_in_t {
float3 position : POSITION;
float4 color : COLOR;
float2 uv : UV;
float4 normal : NORMAL;
};
struct ps_in_t {
float4 position : SV_POSITION;
float4 color : COLOR;
float2 uv : UV;
float4 normal : NORMAL;
float3 world_position : WORLD;
};
ps_in_t VertexFunction(vs_in_t input_vertex) {
ps_in_t output;
float4 local = float4(input_vertex.position, 1.0f);
float4 normal = input_vertex.normal;
float4 world = mul(local, g_MODEL);
float4 view = mul(world, g_VIEW);
float4 clip = mul(view, g_PROJECTION);
output.position = clip;
output.color = input_vertex.color;
output.uv = input_vertex.uv;
output.normal = normal;
output.world_position = world.xyz;
return output;
}

Related

How to pass information from vertex shader to fragment shader if there is a geometry shader active?

Before adding a geometry shader I declared a variable in the vertex shader:
out vec3 normal;
To be received by the fragment shader as:
in vec3 normal;
However if I add a geometry shader to the program, the linker tells me that normal has not been declared as an output from the prvious stage. But I am not sure how to recieve nor send the output in the geometry shader.
The outputs of the verrtex shader ar inputs to the geometry shader and the outputs of the geoemtry shader are inputs to the fragment shader.
Thei inputs to the geometry shader will be an array of the length of the primitive's vertex count. (See Geometry Shader - Inputs).
This means you have to declare an input array and an output in the geometry shader:
in vec3 normal[];
out vec3 geo_normal;
Pass the input to the output:
geo_normal = normal[i];
EmitVertex();
Declare an input in the fragment shader:
in vec3 geo_normal;
An option would be to use Layout Qualifiers
Geometry shader:
in vec3 normal[]; // <---- array
out layout(location=1) vec3 geo_normal;
geo_normal = normal[i];
EmitVertex();
Fragment shader:
in layout(location=1) vec3 normal; // link by layout location 1 and not by name
Another option would be to use Interface Blocks:
Vertex shader:
out TData
{
vec3 normal;
} outData;
outData.normal = .....;
Geometry shader:
in TData
{
vec3 normal;
} inData[]; // <---- array
out TData
{
vec3 normal;
} outData;
outData.normal = inData.normal[i];
EmitVertex();
Fragment shader:
in TData
{
vec3 normal;
} inData;
..... = inData.normal;

Normal mapping gone horribly wrong

I tried to implement normal mapping in my opengl application but I can't get it to work.
This is the diffuse map (which I add a brown color to) and this is the normal map.
In order to get the tangent and bitangent (in other places called binormals?) vectors, I run this function for every triangle in my mesh:
void getTangent(const glm::vec3 &v0, const glm::vec3 &v1, const glm::vec3 &v2,
const glm::vec2 &uv0, const glm::vec2 &uv1, const glm::vec2 &uv2,
std::vector<glm::vec3> &vTangents, std::vector<glm::vec3> &vBiangents)
{
// Edges of the triangle : postion delta
glm::vec3 deltaPos1 = v1-v0;
glm::vec3 deltaPos2 = v2-v0;
// UV delta
glm::vec2 deltaUV1 = uv1-uv0;
glm::vec2 deltaUV2 = uv2-uv0;
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
glm::vec3 tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y)*r;
glm::vec3 bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x)*r;
for(int i = 0; i < 3; i++) {
vTangents.push_back(tangent);
vBiangents.push_back(bitangent);
}
}
After that, I call glBufferData to upload the vertices, normals, uvs, tangents and bitangents to the GPU.
The vertex shader:
#version 430
uniform mat4 ProjectionMatrix;
uniform mat4 CameraMatrix;
uniform mat4 ModelMatrix;
in vec3 vertex;
in vec3 normal;
in vec2 uv;
in vec3 tangent;
in vec3 bitangent;
out vec2 fsCoords;
out vec3 fsVertex;
out mat3 TBNMatrix;
void main()
{
gl_Position = ProjectionMatrix * CameraMatrix * ModelMatrix * vec4(vertex, 1.0);
fsCoords = uv;
fsVertex = vertex;
TBNMatrix = mat3(tangent, bitangent, normal);
}
Fragment shader:
#version 430
uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform mat4 ModelMatrix;
uniform vec3 CameraPosition;
uniform struct Light {
float ambient;
vec3 position;
} light;
uniform float shininess;
in vec2 fsCoords;
in vec3 fsVertex;
in mat3 TBNMatrix;
out vec4 color;
void main()
{
//base color
const vec3 brownColor = vec3(153.0 / 255.0, 102.0 / 255.0, 51.0 / 255.0);
color = vec4(brownColor * (texture(diffuseMap, fsCoords).rgb + 0.25), 1.0);//add a fixed base color (0.25), because its dark as hell
//general vars
vec3 normal = texture(normalMap, fsCoords).rgb * 2.0 - 1.0;
vec3 surfacePos = vec3(ModelMatrix * vec4(fsVertex, 1.0));
vec3 surfaceToLight = normalize(TBNMatrix * (light.position - surfacePos)); //unit vector
vec3 eyePos = TBNMatrix * CameraPosition;
//diffuse
float diffuse = max(0.0, dot(normal, surfaceToLight));
//specular
float specular;
vec3 incidentVector = -surfaceToLight; //unit
vec3 reflectionVector = reflect(incidentVector, normal); //unit vector
vec3 surfaceToCamera = normalize(eyePos - surfacePos); //unit vector
float cosAngle = max(0.0, dot(surfaceToCamera, reflectionVector));
if(diffuse > 0.0)
specular = pow(cosAngle, shininess);
//add lighting to the fragment color (no attenuation for now)
color.rgb *= light.ambient;
color.rgb += diffuse + specular;
}
The image I get is completely incorrect. (light positioned on camera)
What am I doing wrong here?
My bet is on the color setting/mixing in fragment shader...
you are setting output color more then once
If I remember correctly on some gfx drivers that do a big problems for example everything after the line
color = vec4(brownColor * (texture(diffuseMap, fsCoords).rgb + 0.25), 1.0);//add a fixed base color (0.25), because its dark as hell
could be deleted by driver ...
you are adding color and intensities instead of color*intensity
but I could overlook someting.
try just normal/bump shading at first
Ignore ambient,reflect,specular... and then if it works add the rest one by one. Always check the shader's compilation logs
Too lazy to further analyze your code, so here is how I do it:
Left size is space ship object (similar to ZXS Elite's Viper) rendered with fixed function. Right side the same (a bit different rotation of object) with GLSL shader's in place and this normal/bump map
[Vertex]
//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
// texture units:
// 0 - texture0 map 2D rgba
// 1 - texture1 map 2D rgba
// 2 - normal map 2D xyz
// 3 - specular map 2D i
// 4 - light map 2D rgb rgb
// 5 - enviroment/skybox cube map 3D rgb
uniform mat4x4 tm_l2g;
uniform mat4x4 tm_l2g_dir;
uniform mat4x4 tm_g2s;
uniform mat4x4 tm_l2s_per;
uniform mat4x4 tm_per;
layout(location=0) in vec3 pos;
layout(location=1) in vec4 col;
layout(location=2) in vec2 txr;
layout(location=3) in vec3 tan;
layout(location=4) in vec3 bin;
layout(location=5) in vec3 nor;
out smooth vec3 pixel_pos;
out smooth vec4 pixel_col;
out smooth vec2 pixel_txr;
//out flat mat3 pixel_TBN;
out smooth mat3 pixel_TBN;
//------------------------------------------------------------------
void main(void)
{
vec4 p;
p.xyz=pos;
p.w=1.0;
p=tm_l2g*p;
pixel_pos=p.xyz;
p=tm_g2s*p;
gl_Position=p;
pixel_col=col;
pixel_txr=txr;
p.xyz=tan.xyz; p.w=1.0; pixel_TBN[0]=normalize((tm_l2g_dir*p).xyz);
p.xyz=bin.xyz; p.w=1.0; pixel_TBN[1]=normalize((tm_l2g_dir*p).xyz);
p.xyz=nor.xyz; p.w=1.0; pixel_TBN[2]=normalize((tm_l2g_dir*p).xyz);
}
//------------------------------------------------------------------
[Fragment]
//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
in smooth vec3 pixel_pos;
in smooth vec4 pixel_col;
in smooth vec2 pixel_txr;
//in flat mat3 pixel_TBN;
in smooth mat3 pixel_TBN;
uniform sampler2D txr_texture0;
uniform sampler2D txr_texture1;
uniform sampler2D txr_normal;
uniform sampler2D txr_specular;
uniform sampler2D txr_light;
uniform samplerCube txr_skybox;
const int _lights=3;
uniform vec3 light_col0=vec3(0.1,0.1,0.1);
uniform vec3 light_dir[_lights]= // direction to local star in ellipsoid space
{
vec3(0.0,0.0,+1.0),
vec3(0.0,0.0,+1.0),
vec3(0.0,0.0,+1.0),
};
uniform vec3 light_col[_lights]= // local star color * visual intensity
{
vec3(1.0,0.0,0.0),
vec3(0.0,1.0,0.0),
vec3(0.0,0.0,1.0),
};
out layout(location=0) vec4 frag_col;
const vec4 v05=vec4(0.5,0.5,0.5,0.5);
const bool _blend=false;
const bool _reflect=true;
//------------------------------------------------------------------
void main(void)
{
float a=0.0,b,li;
vec4 col,blend0,blend1,specul,skybox;
vec3 normal;
col=(texture2D(txr_normal,pixel_txr.st)-v05)*2.0; // normal/bump maping
// normal=pixel_TBN*col.xyz;
normal=pixel_TBN[0];
blend0=texture(txr_texture0,pixel_txr.st);
blend1=texture(txr_texture1,pixel_txr.st);
specul=texture(txr_specular,pixel_txr.st);
skybox=texture(txr_skybox,normal);
if (_blend)
{
a=blend1.a;
blend0*=1.0-a;
blend1*=a;
blend0+=blend1;
blend0.a=a;
}
col.xyz=light_col0; col.a=0.0; li=0.0; // normal shading (aj s bump mapingom)
for (int i=0;i<_lights;i++)
{
b=dot(light_dir[i],normal.xyz);
if (b<0.0) b=0.0;
// b*=specul.r;
li+=b;
col.xyz+=light_col[i]*b;
}
col*=blend0;
if (li<=0.1)
{
blend0=texture2D(txr_light,pixel_txr.st);
blend0*=1.0-a;
blend0.a=a;
col+=blend0;
}
if (_reflect) col+=skybox*specul.r;
col*=pixel_col;
if (col.r<0.0) col.r=0.0;
if (col.g<0.0) col.g=0.0;
if (col.b<0.0) col.b=0.0;
a=0.0;
if (a<col.r) a=col.r;
if (a<col.g) a=col.g;
if (a<col.b) a=col.b;
if (a>1.0)
{
a=1.0/a;
col.r*=a;
col.g*=a;
col.b*=a;
}
frag_col=col;
}
//------------------------------------------------------------------
These source codes are bit old and mix of different things for specific application
So extract only what you need from it. If you are confused with the variable names then comment me...
tm_ stands for transform matrix
l2g stands for local coordinate system to global coordinate system transform
dir means that transformation changes just direction (offset is 0,0,0)
g2s stands for global to screen ...
per is perspective transform ...
The GLSL compilation log
You have to obtain its content programaticaly after compilation of your shader's (not application!!!). I do it with calling the function glGetShaderInfoLog for every shader,program I use ...
[Notes]
Some drivers optimize "unused" variables. As you can see at the image txr_texture1 is not found even if the fragment shader has it in code but the blending is not used in this App so driver deleted it on its own...
Shader logs can show you much (syntax errors, warnings...)
there are few GLSL IDEs for making shader's easy but I prefer my own because I can use in it the target app code directly. Mine looks like this:
each txt window is a shader source (vertex,fragment,...) the right bottom is clipboard, left top is shader's log after last compilation and left bottom is the preview. I managed to code it like Borland style IDE (with the keys also and syntax highlight) the other IDEs I saw look similar (different colors of coarse:)) anyway if you want to play with shader's download such App or do it your self it will help a lot...
There could be also a problem with TBN creation
You should visually check if the TBN vectors (tangent,binormal,normal) correspond to object surface by drawing colored lines at each vertex position. Just to be sure... something like this:
I will try to make your code work. Have you tried it with moving camera?
I cannot see anywhere that you have transformed the TBNMatrix with the transform, view and model matrices. Did you try with the vec3 normal = TBNMatrix[2]; original normals? (Fragment shader)
The following might help. In the Vertex shader you have:
uniform mat4 ProjectionMatrix;
uniform mat4 CameraMatrix;
uniform mat4 ModelMatrix;
However here, only these 3 matrices should be used:
uniform mat4 PCM;
uniform mat4 MIT; //could be mat3
uniform mat4 ModelMatrix; //could be mat3
It is more efficient to calculate the product of those matrices on CPU (and yields the same because matrix multiplication is associative). Then this product, the PCM can be used as to calculate the new position with one multiplication per vertex:
gl_Position = PCM * vec4(vertex, 1.0);
The MIT is the inverse transpose of the ModelMatrix, you have to calculate it on the CPU. This can be used the transform the normals:
vec4 tang = ModelMatrix*vec4(tangent,0);
vec4 bita= ModelMatrix*vec4(bitangent,0);
vec4 norm= PCMIT*vec4(tangent,0);
TBNMatrix = mat3(normalize(tang.xyz), normalize(bita.xyz), normalize(normal.xyz));
I am not sure what happens to the tangent and bitangent, but this way the normal will stay perpendicular to them. It is easy to prove. Here I use a ° b as the skalar product of a and b vectors. So let n be some normal, and a is some vektor on the surface (eg. {bi}tangent, edge of a triangle), and let A be any transformation. Then:
0 = a n = A^(-1) A a ° n = A a ° A^(-T) n = 0
Where I used the equality A x ° y = x ° A^T y. Therefore if a is perpendicular to n, then A a is perpendicular to A^(-T) n, so we have to transform it with the matrix's inverse transpose.
However, the normal should have a length of 1, so after the transformations, it should be normalized.
You can get also get perpendicular normal by doing this:
vec3 normal = normalize(cross(tangent, bitangent));
Where cross(a,b) is the function that calculates cross product of a and b, witch is always perpendicular to both a and b.
Sorry for my English :)

GLSL normals change with a camera rotation

I know that same questions were asked many times, but unfortunately I am unable to find the source of my problem.
With help of tutorials I've written a small GLSL shader. Right now it can work with ambient light and load normals from normal map. The issue is that directional light seems to be dependent on my viewing angle.
Here are my shaders:
//Vertex Shader
#version 120
attribute vec3 position;
attribute vec2 texCoord;
attribute vec3 normal;
attribute vec3 tangent;
varying vec2 texCoord0;
varying mat3 tbnMatrix;
uniform mat4 transform;
void main(){
gl_Position=transform * vec4(position, 1.0);
texCoord0 = texCoord;
vec3 n = normalize((transform*vec4(normal,0.0)).xyz);
vec3 t = normalize((transform*vec4(tangent,0.0)).xyz);
t=normalize(t-dot(t,n)*n);
vec3 btTangent=cross(t,n);
tbnMatrix=transpose(mat3(t,btTangent,n));
}
//Fragment Shader
#version 120
varying vec2 texCoord0;
varying mat3 tbnMatrix;
struct BaseLight{
vec3 color;
float intensity;
};
struct DirectionalLight{
BaseLight base;
vec3 direction;
};
uniform sampler2D diffuse;
uniform sampler2D normalMap;
uniform vec3 ambientLight;
uniform DirectionalLight directionalLight;
vec4 calcLight(BaseLight base, vec3 direction,vec3 normal){
float diffuseFactor=dot(normal,normalize(direction));
vec4 diffuseColor = vec4(0,0,0,0);
if(diffuseFactor>0){
diffuseColor=vec4(base.color,1.0)* base.intensity *diffuseFactor;
}
return diffuseColor;
}
vec4 calcDirectionalLight(DirectionalLight directionalLight ,vec3 normal){
return calcLight(directionalLight.base,directionalLight.direction,normal);
}
void main(){
vec3 normal =tbnMatrix*(255.0/128.0* texture2D(normalMap,texCoord0).xyz-255.0/256.0);
vec4 totalLight = vec4(ambientLight,0) +calcDirectionalLight(directionalLight, normal);
gl_FragColor=texture2D(diffuse,texCoord0)*totalLight;
}
"transform" matrix that I send to the shader is summarily computed this way:
viewProjection=m_perspective* glm::lookAt(CameraPosition,CameraPosition+m_forward,m_up);
glm::mat4 transform = vievProjection * object_matrix;
"object_matrix" is a matrix that I get directly from physics engine.(I think it's the matrix that defines position and rotation of the object in the world space, correct me if I'm wrong.)
And I guess that "transform" matrix is computed correctly, since all the objects are drawn in the right positions. It looks like the problem is related to normals because if I set gl_FragColor = vec4(normal, 0) the color is also changing with camera rotation.
I would greatly appreciate if anyone could point me to my mistake

DirectX FVF-like GLSL Shaders

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!

Why does my OpenGL Phong shader behave like a flat shader?

I've been learning OpenGL for the past couple of weeks and I've run into some trouble implementing a Phong shader. It appears to do no interpolation between vertexes despite my use of the smooth qualifier. Am I missing something here? To give credit where credit is due, the code for the vertex and fragment shaders cribs heavily from the OpenGL SuperBible Fifth Edition. I would highly recommend this book!
Vertex Shader:
#version 330
in vec4 vVertex;
in vec3 vNormal;
uniform mat4 mvpMatrix; // mvp = ModelViewProjection
uniform mat4 mvMatrix; // mv = ModelView
uniform mat3 normalMatrix;
uniform vec3 vLightPosition;
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
void main(void) {
vVaryingNormal = normalMatrix * vNormal;
vec4 vPosition4 = mvMatrix * vVertex;
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
vVaryingLightDir = normalize(vLightPosition - vPosition3);
gl_Position = mvpMatrix * vVertex;
}
Fragment Shader:
#version 330
out vec4 vFragColor;
uniform vec4 ambientColor;
uniform vec4 diffuseColor;
uniform vec4 specularColor;
smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;
void main(void) {
float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));
vFragColor = diff * diffuseColor;
vFragColor += ambientColor;
vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir),normalize(vVaryingNormal)));
float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection));
if(diff != 0) {
float fSpec = pow(spec, 32.0);
vFragColor.rgb += vec3(fSpec, fSpec, fSpec);
}
}
This (public domain) image from Wikipedia shows exactly what sort of image I'm getting and what I'm aiming for -- I'm getting the "flat" image but I want the "Phong" image.
Any help would be greatly appreciated. Thank you!
edit: If it makes a difference, I'm using PyOpenGL 3.0.1 and Python 2.6.
edit2:
Solution
It turns out the problem was with my geometry; Kos was correct. For anyone else that's having this problem with Blender models, Kos pointed out that doing Edit->Faces->Set Smooth does the trick. I found that Wings 3D worked "out of the box."
As an addition to this answer, here is a simple geometry shader which will let you visualize your normals. Modify the accompanying vertex shader as needed based on your attribute locations and how you send your matrices.
But first, a picture of a giant bunny head from our friend the Stanford bunny as an example of the result !
Major warning: do note that I get away with transforming the normals with the modelview matrix instead of a proper normal matrix. This won't work correctly if your modelview contains non uniform scaling. Also, the length of your normals won't be correct but that matters little if you just want to check their direction.
Vertex shader:
#version 330
layout(location = 0) in vec4 position;
layout(location = 1) in vec4 normal;
layout(location = 2) in mat4 mv;
out Data
{
vec4 position;
vec4 normal;
vec4 color;
mat4 mvp;
} vdata;
uniform mat4 projection;
void main()
{
vdata.mvp = projection * mv;
vdata.position = position;
vdata.normal = normal;
}
Geometry shader:
#version 330
layout(triangles) in;
layout(line_strip, max_vertices = 6) out;
in Data
{
vec4 position;
vec4 normal;
vec4 color;
mat4 mvp;
} vdata[3];
out Data
{
vec4 color;
} gdata;
void main()
{
const vec4 green = vec4(0.0f, 1.0f, 0.0f, 1.0f);
const vec4 blue = vec4(0.0f, 0.0f, 1.0f, 1.0f);
for (int i = 0; i < 3; i++)
{
gl_Position = vdata[i].mvp * vdata[i].position;
gdata.color = green;
EmitVertex();
gl_Position = vdata[i].mvp * (vdata[i].position + vdata[i].normal);
gdata.color = blue;
EmitVertex();
EndPrimitive();
}
}
Fragment shader:
#version 330
in Data
{
vec4 color;
} gdata;
out vec4 outputColor;
void main()
{
outputColor = gdata.color;
}
Hmm... You're interpolating the normal as a varying variable, so the fragment shader should receive the correct per-pixel normal.
The only explanation (I can think of) of the fact that you're having the result as on your left image is that every fragment on a given face ultimately receives the same normal. You can confirm it with a fragment shader like:
void main() {
vFragColor = normalize(vVaryingNormal);
}
If it's the case, the question remains: Why? The vertex shader looks OK.
So maybe there's something wrong in your geometry? What is the data which you send to the shader? Are you sure you have correctly calculated per-vertex normals, not just per-face normals?
The orange lines are normals of the diagonal face, the red lines are normals of the horizontal face.
If your data looks like the above image, then even with a correct shader you'll get flat shading. Make sure that you have correct per-vertex normals like on the lower image. (They are really simple to calculate for a sphere.)