I want to add directional light to my scene using OpenGL and GLSL. The problem is that the theoretically correct way to do so has the wrong results.
In the vertex shader I do the following:
The direction of the light is given in world-coordinates and transformed using the viewMatrix to camera-coordinates. The normal of the vertex is transformed using the normal-matrix to camera-coordinates.
void main () {
vary_textureCoord = attribute_textureCoord;
vary_normal = mat3(normalMatrix) * attribute_normal;
vary_directionalLight_direction = viewMatrix * vec4(lightDir, 1.0);
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(attribute_position, 1.0);
}
So both vectors are in camera-coordinates and passed to the fragment shader. The fragment shader calculates the light intensity using the normal and the direction of the light.
void main () {
vec3 normalizedNormal = normalize(vary_normal);
vec4 color = texture(tex, vary_textureCoord);
float directionalLightIntensity = max(0.0, dot(normalizedNormal, normalize(-vary_directionalLight_direction.xyz)));
out_color = color * directionalLightIntensity;
}
This shader leads to the result that the light is not static but moves along with the camera. Changing the vertex shader using this line instead:
vary_directionalLight_direction = transpose(inverse(viewMatrix)) * vec4(lightDir, 1.0);
has the desired results.
So what am I doing wrong or where do I have a misunderstanding?
Here the full shader codes:
Vertexshader:
# version 330
layout(location = 0) in vec3 attribute_position;
layout(location = 2) in vec2 attribute_textureCoord;
layout(location = 3) in vec3 attribute_normal;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform mat4 normalMatrix;
uniform vec3 lightDir;
out vec2 vary_textureCoord;
out vec3 vary_normal;
out vec4 vary_directionalLight_direction;
void main () {
vary_textureCoord = attribute_textureCoord;
vary_normal = mat3(normalMatrix) * attribute_normal;
vary_directionalLight_direction = viewMatrix * vec4(lightDir, 1.0);
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(attribute_position, 1.0);
}
Fragmentshader:
# version 330
in vec2 vary_textureCoord;
in vec3 vary_normal;
in vec4 vary_directionalLight_direction;
uniform sampler2D tex;
out vec4 out_color;
void main () {
vec3 normalizedNormal = normalize(vary_normal);
vec4 color = texture(tex, vary_textureCoord);
float directionalLightIntensity = max(0.0, dot(normalizedNormal, normalize(-vary_directionalLight_direction.xyz)));
out_color = color * directionalLightIntensity;
}
A transformation matrix looks like this:
( X-axis.x, X-axis.y, X-axis.z, 0 )
( Y-axis.x, Y-axis.y, Y-axis.z, 0 )
( Z-axis.x, Z-axis.y, Z-axis.z, 0 )
( trans.x, trans.y, trans.z, 1 )
If you want to transform a position, then you have to apply the full transformation matrix to the position.
mat4 viewMatrix;
vec3 pos;
vec4 viewPos = viewMatrix * vec4(pos, 1.0);
This applies the orientation and the position of the view to the position pos.
But a direction has no origin. A direction has only an orientation. This means, if you want to transform a direction vector, by the view, then you have to multiply it by the upper left 3*3 part of the 4*4 viewMatrix:
vary_directionalLight_direction = vec4(mat3(viewMatrix) * lightDir, 1.0);
Related
Currently I am rendering mesh triangles like this:
// draw the same polygons again
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
shader.setVec3("objectColor", obj_color);
glDrawElements(GL_TRIANGLES, static_cast<unsigned int>(indices.size()), GL_UNSIGNED_INT, 0);
The problem with this code is that I am setting object color inside shader for the full mesh.
What would be a good way to render one single mesh whose faces have different colors?
For now I only know how to set vertex colors, and pass it the fragment shader.
What are the most common ways to set individual face colors?
I only think about duplicating mesh vertices twice to avoid vertex color interpolation.
My current shader looks like this:
Vertex Shader:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
out vec3 LightPos;
uniform vec3 lightPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
FragPos = vec3(view * model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(view * model))) * aNormal;
LightPos = vec3(vec4(lightPos, 1.0)); // Transform world-space light position to view-space light position
// FragPos = vec3(model * vec4(aPos, 1.0));
//Normal = mat3(transpose(inverse(model))) * aNormal;
// gl_Position = projection * view * vec4(FragPos, 1.0);
}
Fragment Shader:
#version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
in vec3 LightPos;
// extra in variable, since we need the light position in view space we calculate this in the vertex shader
uniform vec3 lightColor;
uniform vec3 objectColor;
uniform float f;
uniform float transparency;
void main()
{
//flat shading
// vec3 x_ = dFdx(FragPos);
// vec3 y_= dFdy(FragPos);
// vec3 normal_ = cross(x_, y_);
// vec3 norm_ = normalize(normal_);
// ambient
float ambientStrength = 0.75;
vec3 ambient = ambientStrength * lightColor;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(LightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);//change "norm_" to "norm" avoid the performance warning and have unwelded view
vec3 diffuse = diff * lightColor;
// specular
float specularStrength = 0.01;
vec3 viewDir = normalize(-FragPos); // the viewer is always at (0,0,0) in view-space, so viewDir is (0,0,0) - Position => -Position
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
vec3 shading = (ambient + diffuse + specular)*objectColor;
//float f = 0.75;
float r_interpolated = shading[0] + f * (objectColor[0] - shading[0]);
float g_interpolated = shading[1] + f * (objectColor[1] - shading[1]);
float b_interpolated = shading[2] + f * (objectColor[2] - shading[2]);
vec3 result = vec3(r_interpolated,g_interpolated,b_interpolated);
FragColor = vec4(result, transparency);
}
You can use the flat Interpolation qualifier:
The value will not be interpolated. The value given to the fragment shader is the value from the Provoking Vertex for that primitive.
Vertex shader
// [...]
layout (location = 0) in vec3 aColor;
flat out vec3 vColor;
void main()
{
vColor = aColor;
// [...]
}
Fragment shader
// [...]
flat in vec3 vColor;
void main()
{
FragColor = vec4(vColor, 1.0);
}
With this implementation, the entire triangle primitive is rendered with one color. If you find an intelligent system for assigning the color attributes to the vertices, you can render all triangles with different colors. e.g. 2 tringles with the indices 0-1-2 and 1-2-3. The color attribute of vertex 0 defines the color of the first triangle and the color attribute of vertex 1 defines the color of the 2nd triangle.
An alternative way would be to create an array of colors for each triangle primitive and store this color array in a Shader Storage Buffer Object. Use gl_VertexID to address the color in the vertex shader.
layout(std430, binding = 0) buffer primitiveColors
{
vec4 colors[];
};
void main()
{
vColor = colors[gl_VertexID / 3];
// [...]
}
I've been trying for some time now to debug this simple Phong-Shader and just came up short. The diffuse part is fine but the specular is not. It doesn't move with the camera.
Here's from one side:
And here from the other:
As far as I can see, I did convert the Position to Viewspace, but, apparently, I made another mistake of some kind.
Vertex Shader:
#version 330
#extension GL_ARB_explicit_attrib_location : enable
layout(location=0) in vec3 aPosition;
layout(location=1) in vec3 aNormal;
out vec3 vPosition;
out vec3 vNormal;
uniform mat4 uModel;
uniform mat4 uView;
uniform mat4 uProjection;
void main(void)
{
vPosition = vec3(uModel * vec4(aPosition,1.0f));
vNormal = vec3(uModel * vec4(aNormal, 0.0f));
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
}
And my Fragment Shader
#version 330
out vec4 FragColor;
in vec3 vPosition;
in vec3 vNormal;
uniform mat4 uView;
uniform vec3 uColor;
uniform vec3 uLightpositions[10];
uniform vec3 uLightcolors[10];
uniform float uPhongSpecular;
void main(void)
{
FragColor = vec4(0.4*uColor, 1.0);//Ambient Value
for(int i = 0; i < 5; i++){
vec3 lVec = normalize(uLightpositions[i] - vPosition);
vec3 nVec = normalize(vNormal);
float diffuse = max(dot(lVec,nVec), 0);
FragColor += 0.5* vec4(uLightcolors[i] * diffuse,0.0f);
vec3 rVec = normalize(reflect(lVec,nVec));
vec3 vVec = -normalize(vec3(uView * vec4(vPosition,1.0)));
float specular = 0;
if(dot(rVec,vVec) < 0.0)
{
specular = pow(max(dot(-rVec,vVec),0),uPhongSpecular);
}
FragColor += 0.2*vec4(uLightcolors[i] * specular,0.0f);
}
}
The problem is dot(-rVec, vVec). vVec is a vector in view space, however, rVec is a vector in world space. Convert rVec from world space to view space:
vec3 rVec = normalize(reflect(lVec, nVec));
vec3 rVec = normalize(mat3(uView) * reflect(lVec, nVec));
One thing wrong in addition to Rabbid76's answer is if(dot(rVec,vVec) < 0.0) is testing if the angle between the two is greater than 90 degrees. In other words, you are testing if the specular reflection is visible behind the model. You have to flip that from less-than to greater-than >.
trying to implement shadow. I checked my depth texture on a quad, and it seems correct, but the shadow is not displaying. I check my shadow vertex and fragment shaders, and I believe I have done the light space transformation correctly.
Here are my code.
directional light source matrix setup:
//light source states
glm::vec3 Window::lightColor = glm::vec3(0.9f, 0.9f, 0.9f);
glm::vec3 Window::lightDir = glm::vec3(-1.f, -1.f, 0.f);
glm::mat4 Window::lightView = glm::lookAt(glm::vec3(0.f) - glm::normalize(lightDir) * 15.f, glm::vec3(0.0f), glm::vec3(0.f, 1.f, 0.f));
float Window::near_plane = 0.01f;
float Window::far_plane = 50.1f;
float camWidth = 10.f;
glm::mat4 Window::lightProj = glm::ortho(-10.f, 10.f, -10.f, 10.f, Window::near_plane, Window::far_plane);
glm::mat4 Window::lightProjView = lightProj * lightView;
shadow drawing logic:
void Renderer::drawWithShadow(Object* obj) {
//set shader uniforms
Shader* shader = shadowShader;
shader->bind();
shader->setUniformMat4("model", obj->model);
shader->setUniformMat4("projView", projView);
shader->setUniformVec3("viewPos", eyePos);
//need another projection matrix
shader->setUniformMat4("lightSpaceMatrix", shadowProjView);
glcheck(glActiveTexture(GL_TEXTURE0));
glcheck(glBindTexture(GL_TEXTURE_2D, textID));
//light uniforms
shader->setUniformVec3("directionalLightDir", directionalLightDir);
shader->setUniformVec3("lightColor", lightColor);
glcheck(glBindVertexArray(obj->vao));
for (auto i = 0; i < obj->meshList.size(); i++) {
Mesh* mesh = obj->meshList[i];
prepMaterial(mesh->material, shader);
glcheck(glDrawElements(GL_TRIANGLES, mesh->size, GL_UNSIGNED_INT, (GLvoid*)(sizeof(GLuint) * mesh->vertexOffset)));
}
}
vert and frag shaders to prepare shadow depth textures
//vertex shader
#version 330 core
layout (location = 0) in vec3 position;
uniform mat4 projView;
uniform mat4 model;
void main() {
gl_Position = projView * model * vec4(position, 1.0);
}
//fragment shader
#version 330 core
void main()
{
}
vert and frag shaders to draw shadows with Phong lighting
//vertex shader
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texCoord;
out VS_OUT {
vec4 fragPos;
vec3 normal;
vec2 texCoord;
vec4 fragPosLightSpace;
} vs_out;
uniform mat4 projView;
uniform mat4 model;
uniform mat4 lightSpaceMatrix;
void main()
{
vs_out.fragPos = model * vec4(position, 1.0);
vs_out.normal = transpose(inverse(mat3(model))) * normal;
vs_out.texCoord = texCoord;
vs_out.fragPosLightSpace = lightSpaceMatrix * vs_out.fragPos;
gl_Position = projView * vs_out.fragPos;
}
//fragment shader
#version 330 core
uniform vec3 viewPos; //just the eye pos
uniform vec3 diffuseFactor; //kd
uniform vec3 ambientColor; //ka
uniform vec3 specColor; //ks
uniform float specHighlight; //ns, the larger this value is, the more apparent the light dot on the surface
uniform float dissolve; //d
//lights
uniform vec3 directionalLightDir;
uniform vec3 pointLightPos;
uniform vec3 lightColor;
uniform sampler2D shadowMap;
//uniform sampler2DShadow shadowMap;
in VS_OUT {
vec4 fragPos;
vec3 normal;
vec2 texCoord;
vec4 fragPosLightSpace;
} fs_in;
out vec4 fragColor;
float ShadowCalculation(vec4 fragPosLightSpace)
{
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
vec2 shadowCoords;
shadowCoords.x = projCoords.x * 0.5 + 0.5;
shadowCoords.y = projCoords.y * 0.5 + 0.5;
float closestDepth = texture(shadowMap, shadowCoords).r;
float currentDepth = projCoords.z * 0.5 + 0.5;
float shadowValue = currentDepth + 0.00001 > closestDepth ? 1.0 : 0.0;
//if(currentDepth < 0.0)
//shadowValue = 0.0;
return shadowValue;
}
void main()
{
vec3 lightDir = normalize(-directionalLightDir);
vec3 norm = normalize(fs_in.normal);
//diffuse lighting
float diffStrength = max(dot(norm, lightDir), 0.0); // this calculates diffuse intensity based on angle
vec3 diffuse = lightColor * diffStrength * diffuseFactor;
//specular
vec3 viewDir = normalize(viewPos - fs_in.fragPos.xyz);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = 0.0;
if(specHighlight > 0.0) { // if specHighlight is < 0, pow might produce undefined result if base is also 0
spec = pow(max(dot(viewDir, reflectDir), 0.0), specHighlight);
}
vec3 specular = spec * specColor * lightColor;
float shadow = ShadowCalculation(fs_in.fragPosLightSpace);
//float shadow = textureProj(shadowMap, fs_in.fragPosLightSpace);
//vec3 result = ambientColor * 0.05 * lightColor + (diffuse + specular)*(1-shadow);
vec3 result = (diffuse + specular)*(1.0 - shadow);
fragColor = vec4(result, 1);
}
with just Phong shading, the scene looks like this:
Phong shading
when the scene is seen from the light source as depth value:
depth texture on quad
when I finally render the scene, it is mostly black; I made sure the far plane covers all of the bunnies:
render shadow
I'm trying to figure out a way to light up my object in a pixelated fashion through the use of shaders.
To ilustrate, my goal is to turn this:
Into this:
I've tried looking up ways to do this through the fragment shader, however, there is no way I can access the local position of a fragment to determine the "fake pixel" it would belong to. I also had the idea to use a geometry shader to create a vertex for each of those boxes, but I'm under suspicion there could be a better way to do this. Would it be possible?
EDIT: These are the shaders currently being used for the object illustrated by the first image:
vertex shader:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTex;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 oColor; //Output of a color
out vec2 oTex; //Output of a Texture
out vec3 oPos; //Output of Position in space for light calculation
out vec3 oNormal; //Output of Normal vector for light calculation.
void main(){
gl_Position = projection * view * model * vec4(aPos, 1.0);
oColor = aColor;
oTex = aTex;
oPos = vec3(model * vec4(aPos, 1.0));
oNormal = vec3(0, 0, -1); //Not being calculated at the moment.
}
fragment shader:
#version 330 core
in vec3 oColor;
in vec2 oTex;
in vec3 oPos;
in vec3 oNormal;
out vec4 FragColor;
uniform sampler2D tex;
uniform vec3 lightColor; //Color of the light on the scene, there's only one
uniform vec3 lightPos; //Position of the light on the scene
void main(){
//Ambient Light Calculation
float ambientStrength = 0.1;
//vec3 ambient = ambientStrength * lightColor * vec3(texture(tex, oTex));
vec3 ambient = ambientStrength * lightColor;
//Diffuse Light Calculation
float diffuseStrength = 1.0;
vec3 norm = normalize(oNormal);
vec3 lightDir = normalize(lightPos - oPos);
float diff = max(dot(norm, lightDir), 0.0);
//vec3 diffuse = diff * lightColor* vec3(texture(tex, oTex)) * diffuseStrength;
vec3 diffuse = diff * lightColor;
//Specular Light Calculation
float specularStrength = 0.25;
float shinnyness = 8;
vec3 viewPos = vec3(0, 0, -10);
vec3 viewDir = normalize(viewPos - oPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), shinnyness);
vec3 specular = specularStrength * spec * lightColor;
//Result Light
vec3 result = (ambient+diffuse+specular) * oColor;
FragColor = vec4(result, 1.0f);
}
The lighting depends on oPos. You need to "cascade" the position. e.g:
vec3 pos = vec3(round(oPos.xy * 10.0) / 10.0, oPos.z);
In the following use pos instead of oPos.
Note that this only works if oPos is a position in the view space, respectively if the XY plane of the oPos` coordinate system is parallel to the XY plane of the view.
Alternatively you can compute the a position depending on gl_FragCoord.
Add a uniform variable with the resolution of the screen:
uniform vec2 resolution;
Compute pos depending on resolution and gl_FragCoord:
vec3 pos = vec3(round(20.0 * gl_FragCoord.xy/resolution.y) / 20.0, oPos.z);
If you want to align the inner squares with the object you need to introduce texture coordinates. Where the bottom left coordinate of the object is (0, 0) and the top right is (1, 1).
I am looking for some help debugging my GLSL phong shading code. Here is my vertex shader:
layout(std140) uniform Matrices {
mat4 model[1024];
};
layout(location = 0) in vec4 vertexCoord;
layout(location = 2) in vec3 vertexNormal;
uniform mat4 view; // from lookAt()
uniform mat4 projection; // perspective projection
out vec3 Position;
out vec3 Normal;
out vec4 lightPosEye;
void main() {
mat4 modelView = view * model[gl_InstanceID];
mat3 normalMatrix = mat3(transpose(inverse(modelView)));
//mat3 normalMatrix = mat3(modelView);
vec4 lightPos = vec4(350, 350, 350, 1);
lightPosEye = modelView * lightPos;
Position = vec3(modelView * vertexCoord);
Normal = normalize(normalMatrix * vertexNormal);
gl_Position = projection * vec4(Position, 1.0);
}
and here is my fragment shader:
in vec3 Position;
in vec3 Normal;
in vec4 lightPosEye;
layout(location = 0) out vec4 FragColor;
vec3 ads() {
vec3 Ka = vec3(0, 0.5, 0);
vec3 Kd = vec3(0, 0.5, 0);
vec3 Ks = vec3(0, 0.1, 0);
vec3 intensity = vec3(0.3, 0.5, 0.0);
float shininess = 0.1;
vec3 n = normalize(Normal);
vec3 s = normalize(vec3(lightPosEye) - Position);
vec3 v = normalize(vec3(-Position));
vec3 r = reflect(-s, n);
return intensity * (Ka + Kd * max(dot(s, n), 0.0) + Ks * pow(max(dot(r,v), 0.0), shininess));
}
void main() {
FragColor = vec4(ads(), 1);
}
Here is a screenshot of the result (I am also rendering the normals with another geometry shader for debugging purposes):
The artifacts on the cube are wrong, and the "shadows" on the circle also moves a bit when I move the camera around (change the view matrix).
Any obvious errors in my GLSL code?