First of all I am sorry for this long post after trying to make this work the whole day.
I have many questions about this especially because I use inheritance in C++ to build lights.
I use directional light as my core model for light since i can give the light direction and it will calculate the light, then on top of it I build point light where i just calculate the vector from light to fragment position, and finally for spot light I use point light with the addition of cut off angle to create spot lights (just ignore whatever is outside the cone). I have tested lights and they work fine with forward rendering but now I would like to change my light model to PBR (basically just change how I calculate light in directional light) and move to differed rendering.
Today i started working on deferred rendering and I can get the position, texture, normal and depth buffers, however i have a problem when trying to render lights.
That was the first problem, the second, since each type of light has it own shader and i build them using polymorphism. My second question is I can loop through each light in C++ and call each light to be renderer or there is another way that i can solve this in shaders.
Prototypes of lights are
EDIT: I fixed a small issue where iw as transforming the render quat with VP projection but still i can not draw anything and i have no idea if FB are working correctlly now. Nvidia opengl debugger is just crashing.
Light(glm::vec3& color, float intensity, float ambient, ShaderProgram& lightShader);
DirectionalLight(glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3& position = glm::vec3(0.0f, 0.0f, 0.0f), float intensity = 1.0f, float ambient = 0.0f, ShaderProgram& lightShader = ShaderProgram("Directional Light"));
PointLight(glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3& position = glm::vec3(0.0f, 0.0f, 0.0f), float intensity = 1.0f, float ambient = 0.0f, LightAttenuation& lightAttenuation = LightAttenuation(), ShaderProgram& lightShader = ShaderProgram("Point Light"));
SpotLight(glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3& position = glm::vec3(0.0f, 0.0f, 0.0f),
My render path looks like this.
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
defferedShader_.startProgram();
defferedShader_.setUniformMat4("VP", camera.getVP());
glBindFramebuffer(GL_FRAMEBUFFER, deferredFbo);
//the scene is small and does not need culling.
for (auto* mesh : world.getMeshes()) {
//mesh->draw(light->getLightShader());
//mesh->draw(activeLight_->getLightShader());
mesh->draw(defferedShader_);
drawCallCounter += mesh->getMeshObjectSize();
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
defferedShader_.stopProgram();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, positionFbo);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, normalFbo);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, albedoFbo);
//This is where i got stuck, I would like to make directional light work then test other lights then test the whole program with more than one light
//for (auto* light : world.getLights()) {
// //glEnable(GL_CULL_FACE);
// //glCullFace(GL_FRONT);
glDisable(GL_DEPTH_TEST);
activeLight_->getLightShader().startProgram();
activeLight_->getLightShader().setUniformMat4("VP", camera.getVP());
activeLight_->getLightShader().setUniformVec3("eyePosition", camera.getCameraPosition());
//activeLight_->getLightShader();
RenderQuad();
activeLight_->getLightShader().stopProgram();
//}
The shader code that i started building is (PS i removed the shadows for now)
Vertex Shader
#version 410 core
#include "../Global/GlobalShader.inc"
#include "../Global/GlobalMesh.inc"
out vec3 Position;
out vec2 TexCoord;
//out vec4 ShadowCoord;
//uniform mat4 ShadowMatrix;
void main() {
Position = position;
TexCoord = texCoord;
//ShadowCoord = ShadowMatrix * vec4(position, 1.0);
gl_Position = VP * vec4(position, 1.0);
}
Fragment shader
One thing that is bothering me is i can not set the uniform values for gPosition, gPosition and gAlbedoSpec even if I use them, and no matter what i change in the shader the output will be the same.
#version 410 core
#include "../Global/GlobalShader.inc"
#include "../Global/GlobalMesh.inc"
#include "../Global/GlobalLight.inc"
//#include "../Global/ShadowSampling.inc"
in vec3 Position;
in vec2 TexCoord;
//in vec4 ShadowCoord;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
float specularStrength = 32.0f; // to be impelemented
out vec4 gl_FragColor;
void main() {
//vec4 lightning = vec4(0.0f);
////vec4 shadowMapping = vec4(0.0f);
//
vec3 FragPos = texture(gPosition, TexCoord).rgb;
vec3 Normal = texture(gNormal, TexCoord).rgb;
vec3 Diffuse = texture(gAlbedoSpec, TexCoord).rgb;
float Specular = texture(gAlbedoSpec, TexCoord).a;
//vec3 Diffuse = texture(gAlbedoSpec, TexCoord).rgb;
//lightning = calculateDirectionalLight(directionalLight.light, directionalLight.position, Normal, Position, specularStrength, eyePosition, material, TexCoord);
//gl_fragColor = vec3(Position, 1.0);
//shadowMapping = calculateShadow(shadowMap, ShadowCoord, directionalLight.light.ambient);
//gl_FragColor = vec4(Diffuse, 1.0);
gl_FragColor = vec4(1.0); //vec4(Diffuse, 1.0);// lightning;//g * shadowMapping;
//gl_FragColor = lightning;// * shadowMapping;
}
in case you want to see global light
struct Light
{
vec3 color;
float intensity;
float ambient;
};
struct DirectionalLight
{
Light light;
vec3 position;
};
struct Attenuation
{
float constant;
float linear;
float quadratic;
};
struct PointLight
{
Light light;
Attenuation atten;
vec3 position;
float range;
};
struct SpotLight
{
PointLight pointLight;
//vec3 lookAt;
vec3 direction;
float cutOff;
};
vec3 GAMMA = vec3(1.0/2.2);
vec4 calculateDirectionalLight(Light light, vec3 direction, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord)
{
vec3 diffuseFactor = ( light.color * material.diffuse * vec3(texture(material.texture.diffuse, texCoord.st)) )
* (light.intensity * clamp(dot(normal, direction), 0.0, 1.0) ) ;
vec3 viewDir = normalize(eyePosition - worldPosition);
vec3 reflectDir = normalize(reflect(-direction, normal));
float specularFactor = pow(clamp(dot(viewDir, reflectDir), 0.0, 1.0), specularIntensity);
vec3 specularColor = ( light.color * material.specular * vec3(texture(material.texture.specular, texCoord.st)) ) * (specularFactor * material.shininess);
return vec4(pow((diffuseFactor + specularColor + light.ambient + material.ambient), GAMMA), 1.0);
}
vec4 calculatePointLight(PointLight pointLight, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord)
{
// DO NOT NORMALIZE lightDirection, WE NEED IT TO CALCULATE THE DISTANCE TO COMPARE RANGE OF LIGHT
vec3 lightDirection = pointLight.position - worldPosition;
float distanceToPoint = length(lightDirection);
// I dont like conditionals in shader, but since this is fragment based lighting i believe
// this will speed-up things insetead of calculating the light
if(distanceToPoint > pointLight.range)
return vec4(0.0,0.0,0.0,0.0);
vec4 light = calculateDirectionalLight(pointLight.light, lightDirection, normal, worldPosition, specularIntensity, eyePosition, material, texCoord);
// light attenuateion explained https://developer.valvesoftware.com/wiki/Constant-Linear-Quadratic_Falloff
// http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Light+Attenuation+Shortcut
float attenuation = max(pointLight.atten.constant
+ pointLight.atten.linear * distanceToPoint
+ pointLight.atten.quadratic * distanceToPoint * distanceToPoint,
1.0);
return light / attenuation;
}
vec4 calculateSpotLight(SpotLight spotLight, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord)
{
vec3 lightDirection = normalize(spotLight.pointLight.position - worldPosition);
float spotFactor = dot(lightDirection, spotLight.direction);
vec4 light = vec4(0.0f);
if(spotFactor > spotLight.cutOff)
{
light = calculatePointLight(spotLight.pointLight, normal, worldPosition, specularIntensity, eyePosition, material, texCoord) * (1.0 - (1.0 - spotFactor)/(1.0 - spotLight.cutOff));
}
return light;
}
Global mesh
struct Texture {
sampler2D diffuse;
sampler2D specular;
sampler2D normal;
sampler2D ambient;
sampler2D height;
//vec2 texCoord;
};
struct Material {
vec3 ambient; // Ka
vec3 diffuse; // Kd
vec3 specular; // Ks
vec3 transmittance; // Tr
vec3 emission; // Ke
float shininess; // Ns
float ior; // Ni
float dissolve; // Dissolve
int illum; // Illum
Texture texture;
};
uniform Material material;
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 normal;
Global Shader
uniform mat4 VP;
uniform mat4 P;
What i am getting now after binding the buffers and running the directional shader is
and just as example to see the scene this is the position buffer
Fixed it. I had the clean buffer and color in the wrong place. It should be after I bind the buffer not at the beginning of each frame.
glEnable(GL_DEPTH_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, deferredFbo);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
defferedShader_.startProgram();
defferedShader_.setUniformMat4("VP", camera.getVP());
.
.
. rest of the code
Related
I have the following shader to draw lightning into 2D quads with normal maps
in vec2 position;
in vec2 texturePosition;
out vec4 fragColor;
layout(std140) uniform ubo {
mat4 uCameraView;
vec3 uLightPosition;
};
uniform sampler2D uTexture0;
uniform sampler2D uTexture1;
// TODO: move into the ubo
const vec4 uLightColor = vec4(1.f, 1.f, 1.f, 1.f);
const vec4 uAmbientColor = vec4(0.2f, 0.2f, 0.2f, 0.2f);
const vec3 uFalloff = vec3(0.1f, 3.f, 20.f);
void main() {
vec4 difusseColor = texture(uTexture0, texturePosition);
vec3 normalMap = texture(uTexture1, texturePosition).rgb;
normalMap.y *= -1;
vec3 lightDirection = vec3(uLightPosition.xy - position.xy, uLightPosition.z);
float direction = length(lightDirection);
vec3 light = normalize(lightDirection);
vec3 normal = normalize(normalMap * 2.f - 1.f);
vec3 diffuseLight = (uLightColor.rgb * uLightColor.a) * dot(normal, light);
vec3 ambient = uAmbientColor.rgb * uAmbientColor.a;
float attenuation = 1.f /
(uFalloff.x + (uFalloff.y * direction) +
(uFalloff.z * direction * direction));
vec3 intensity = ambient + diffuseLight * attenuation;
vec3 color = difusseColor.rgb * intensity;
fragColor = vec4(color, difusseColor.a);
}
But the issue I get is this black hole behind the light
I can remove the black hole with this line of code
vec3 diffuseLight = (uLightColor.rgb * uLightColor.a) * max(dot(normal, light), 0.0f);
But then I get this misaligned light without illumination where the black hole was
This is my vertex shader
void main() {
gl_Position = uCameraView * vec4(vPosition, 0.f, 1.f);
}
uCameraView is a mat4 perspective projection that is done like this
glm::vec3 camera;
glm::mat4 projection =
glm::perspective(glm::radians(45.f), app.aspectRatio, 0.f, 100.f);
projection = glm::scale(projection, size.viewport.scale);
glm::mat4 view = glm::translate(projection, camera);
Is this possible to fix?
I tried following this tutorial https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson6 and in there the light look good.
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 implement normal mapping to my shaders but I keep getting weird results.
I already implemented diffuse and specular light (which are working).
I first calculated the normalized vector from the normal map : Ni = 2 * normalColor - 1
then calculated the binormal vector thanks to the cross vector of normal and tangent.
Finally I created the TBN matrix by transposing (tangent, binormal, normal)
Here is my code and some screenshots.
Vertex shader
#version 330
// Input vertex attributes
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec3 vertexNormal;
in vec4 vertexColor;
in vec4 vertexTangent;
// Input uniform values
uniform mat4 mvp;
// Output vertex attributes (to fragment shader)
out vec2 fragTexCoord;
out vec4 fragColor;
out vec3 fragPosition;
out vec4 fragTangent;
out vec3 fragNormal;
// out vec3 fragBiTangent;
// NOTE: Add here your custom variables
void main()
{
// Send vertex attributes to fragment shader
fragTexCoord = vertexTexCoord;
fragColor = vertexColor;
//added
fragNormal = vertexNormal;
fragPosition = vertexPosition;
fragTangent = vertexTangent;
// Calculate final vertex position
gl_Position = mvp*vec4(vertexPosition, 1.0);
}
Fragment Shader
#version 330
// Input vertex attributes (from vertex shader)
in vec2 fragTexCoord;
in vec4 fragColor;
in vec3 fragNormal;
in vec3 fragPosition;
in vec4 fragTangent;
// Input uniform values
uniform sampler2D texture0; // diffuse texture
uniform vec4 colDiffuse;
uniform vec3 lightPos; // light position
uniform mat4 matModel; // pos, rotation and scaling of object
uniform vec3 viewPos; // eyes position
uniform sampler2D normalMap; // normal texture
// Output fragment color
out vec4 finalColor;
// NOTE: Add here your custom variables
vec3 unHomogenous(vec4 v)
{
return v.xyz/v.w;
}
void main()
{
//=====================LOAD TEXTURES=============================
// Texel color fetching from texture sampler
vec4 texelColor = texture(texture0, fragTexCoord);
// obtain normal from normal map in range [0,1]
vec3 normalColor = texture(normalMap, fragTexCoord).xyz;
//=======================PARAMETERS==============================
// calculate normal in world coordinates
mat3 matNormal = transpose(inverse(mat3(matModel))); //CPU heavy
vec3 worldNormal = normalize(matNormal * fragNormal);
// Calculate the location of this fragment (pixel) in world coordinates
vec3 worldPosition = unHomogenous(matModel * vec4(fragPosition, 1.0));
//=======================NORMAL MAPPING========================
// transform normal vector to range [-1,1]
vec3 normal = normalize(normalColor * 2.0 - 1.0);
normal = normalize(matNormal * normal);
vec3 tangent = normalize(matNormal * fragTangent.xyz);
vec3 binormal = normalize(cross(normal, tangent));
mat3 TBN = mat3(tangent, binormal, worldNormal);
TBN = transpose(TBN);
//=======================DIFFUSING LIGHT===========================
// Shading is calculated by diffuse = (LightVect dot NormalVect) * Diffused color
vec3 ambiant = 0.01 * texelColor.xyz ;
// find light source : L = Lightposition - surfacePosition
vec3 lightDir = normalize(lightPos - worldPosition);
lightDir *= TBN; // NOT WORKING
// diffuse the light with the dot matrix :
float shading = clamp(dot(worldNormal, lightDir), 0.1, 1.0);
vec3 diffuse = shading * texelColor.xyz;
//=======================SPECULAR LIGHTNING====================
//intensity between 0 and 1
float specularStrength = 1;
// //calculate the view direction vector and corresponding reflect vector along the normal axis
vec3 viewDir = normalize(viewPos - worldPosition);
viewDir *= TBN; // NOT WORKING
vec3 reflectDir = reflect(-lightDir, worldNormal);
//Note that we negate the lightDir vector.
//The reflect function expects the first vector to point from the light source towards the fragment's position,
//but the lightDir vector is currently pointing the other way
// calclulate the specula component 32 is the shininess value of the highlight
int shininess = 32;
float spec = pow(clamp(dot(viewDir, reflectDir), 0.1, 1.0), shininess);
vec3 lightColor = vec3(1.0,1.0,1.0);
vec3 specular = specularStrength * spec * texelColor.xyz;
//=======================RENDER================================
finalColor = vec4(ambiant + diffuse + specular, 1.0);
}
main.cpp
#include "raylib.h"
#include "rlgl.h"
#include <math.h>
#include <raymath.h>
int main(void)
{
// Initialization
//[...]
//=====================WALL=======================//
Vector3 position = { -2.5f, 3.0f, 0.0f };
Model model = LoadModel("assets/models/wall.obj");
Texture2D texture = LoadTexture("assets/textures/cgaxis_pbr_17_stone_wall_5_diffuse.png");
// Set normal mapping
Texture2D normal_texture = LoadTexture("assets/textures/cgaxis_pbr_17_stone_wall_5_normal.png");
model.materials[0].maps[MATERIAL_MAP_NORMAL].texture = normal_texture;
//==========Generate mesh and diffuse texture=========//
GenMeshTangents(model.meshes);
model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set map diffuse texture
//===================SELECT SHADERS====================//
// Shader shader = LoadShader("assets/shaders/base.vs", "assets/shaders/base.fs"); // diffuse light
// Shader shader = LoadShader("assets/shaders/specular.vs", "assets/shaders/specular.fs"); // diff + specular
Shader shader = LoadShader("assets/shaders/normal_mapping.vs", "assets/shaders/normal_mapping.fs"); // diff + spec + normal mapping
// Set shader effect to 3d model
model.materials[0].shader = shader;
//==================Light======================//
Vector3 sunPos = {0.0f, 2.0f, 0.0f };
float rotation = 90.0f;
float radius = 5.0f;
// Diffuse light
int lightPosLoc = GetShaderLocation(shader, "lightPos");
float lightPos[] = {sunPos.x, sunPos.y, sunPos.z};
SetShaderValue(shader, lightPosLoc, lightPos, SHADER_UNIFORM_VEC3);
//specular light
int specularPosLoc = GetShaderLocation(shader, "viewPos");
float specularPos[] = {camera.position.x, camera.position.y, camera.position.z};
SetShaderValue(shader, specularPosLoc, specularPos, SHADER_UNIFORM_VEC3);
//RUN & draw
//[...]
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
diffuse + specular
normal mapping
The SSAO in our engine seems to be working, however I cannot get the SSAO to work with shadow mapping. Here is a screenshot of the bug I am currently having when shadows are applied....
With shadows applied
But also, depending on the camera view and camera position, random shadows sometimes appear...
Random Shadows depending on camera view and position
Here is the gbuffer vertex shader..
#version 330 core
layout (location = 0) in vec3 positions;
layout (location = 1) in vec2 texCoords;
layout (location = 2) in vec3 normals;
out vec3 FragPos;
out vec3 ShadowFragPos;
out vec2 TexCoords;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
void main()
{
vec4 viewPos = view * model * vec4(positions, 1.0);
FragPos = viewPos.xyz;
TexCoords = texCoords;
mat3 normalMatrix = transpose(inverse(mat3(view * model)));
Normal = normalMatrix * normals;
gl_Position = proj * viewPos;
}
Here is the lighting shader..
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
uniform sampler2D gShadowmap;
uniform sampler2D gSsao;
uniform vec3 cameraPos;
uniform mat4 lightSpaceMatrix;
vec3 Normal;
vec3 FragPos;
uniform vec3 lightPos;
float calculate_shadows(vec4 light_space_pos)
{
// perform perspective divide
vec3 projCoords = light_space_pos.xyz / light_space_pos.w;
// transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
// get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
float closestDepth = texture(gShadowmap, projCoords.xy).r;
// get depth of current fragment from light's perspective
float currentDepth = projCoords.z;
// check whether current frag pos is in shadow
vec3 lightDir = normalize(vec3(2.0f, 4.0f, 1.0f) - FragPos);
float bias = max(0.05 * (1.0 - dot(Normal, lightDir)), 0.005);
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(gShadowmap, 0);
// 8x8 kernel PCF
float x;
float y;
for (y = -3.5; y <= 3.5 ; y += 1.0)
{
for (x = -3.5; x <= 3.5 ; x += 1.0)
{
float pcfDepth = texture(gShadowmap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 64.0;
return shadow;
}
void main(void)
{
FragPos = texture(gPosition, TexCoords).rgb;
Normal = texture(gNormal, TexCoords).rgb;
vec3 Diffuse = texture(gAlbedoSpec, TexCoords).rgb;
float Specular = texture(gAlbedoSpec, TexCoords).a;
float AmbientOcclusion = texture(gSsao, TexCoords).r;
vec3 lighting = vec3(0.3 * Diffuse * AmbientOcclusion);
vec3 viewDir = normalize(-FragPos);
vec3 lightDir = normalize(lightPos - FragPos);
vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * vec3(1.0f, 0.5f, 0.3f);
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(Normal, halfwayDir), 0.0), 8.0);
vec3 specular = vec3(1.0f, 0.5f, 0.3f) * spec * Specular;
float shadow = calculate_shadows(lightSpaceMatrix * vec4(FragPos, 1.0));
lighting += ((1.0 - shadow) * (diffuse + specular));
FragColor = vec4(lighting, 1.0f);
}
The textures are binded in the light pass as follows..
// bind the positions texture and store in the first texture slot/unit
glActiveTexture(GL_TEXTURE0); // texture unit 0
glBindTexture(GL_TEXTURE_2D, gbuffer.gPositions); // geometry positions
// bind the normals texture and store in the second texture slot/unit
glActiveTexture(GL_TEXTURE1); // texture unit 1
glBindTexture(GL_TEXTURE_2D, gbuffer.gNormals); // geometry normals
// bind the albedo & specular texture and store in the third texture slot/unit
glActiveTexture(GL_TEXTURE2); // texture unit 2
glBindTexture(GL_TEXTURE_2D, gbuffer.gAlbedoSpec); // geometry albedospec
// bind the albedo & specular texture and store in the third texture slot/unit
glActiveTexture(GL_TEXTURE3); // texture unit 3
glBindTexture(GL_TEXTURE_2D, gbuffer.gShadowmap); // geometry albedospec
glActiveTexture(GL_TEXTURE4); // texture unit 2
glBindTexture(GL_TEXTURE_2D, gbuffer.ssaoColorBuffer); // geometry albedospec
Finally, here is the calculation of the lightSpaceMatrix..
light_projection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, 1.0f, 7.5f);
light_view = glm::lookAt(glm::vec3(0.0f, 4.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
light_space_matrix = light_projection * light_view;
Any ideas why this could be happening? how do I get shadows to work with SSAO?
any help is much appreciated.
FragPos is a camera view space position.
light_space_pos, the input parameter to calculate_shadows has to be a clip space coordinate, as seen from the light source.
This mean that when you do
float shadow = calculate_shadows(lightSpaceMatrix * vec4(FragPos, 1.0));
lightSpaceMatrix has to be the transformation from the camera view space to the clip space of the light source.
To do so, you have to do 3 transformations:
camera view space to world space. This can bed done by the inverse view matrix.
world space to light space, which is the transformation by light_view.
light view space to light clip space, is the transformation by light_projection.
So the setting of light_space_matrix = light_projection * light_view; is not sufficient, it has to be
light_space_matrix = light_projection * light_view * glm::inverse(view);
Recently I added deferred shading support in my engine; however I ran into some attenuation issues:
As you can see, when I'm rendering the light volume (sphere), it doesn't blend nicely with the ambient part of the image !
Here is how I declare my point light:
PointLight pointlight;
pointlight.SetPosition(glm::vec3(0.0, 6.0, 0.0));
pointlight.SetIntensity(glm::vec3(1.0f, 1.0f, 1.0f));
Here is how I compute the light sphere radius:
Attenuation attenuation = pointLights[i].GetAttenuation();
float lightMax = std::fmaxf(std::fmax(pointLights[i].GetIntensity().r, pointLights[i].GetIntensity().g),
pointLights[i].GetIntensity().b);
float pointLightRadius = (-attenuation.linear +
std::sqrtf(std::pow(attenuation.linear, 2.0f) - 4.0f * attenuation.exponential *
(attenuation.constant - (256.0f / 5.0f) * lightMax))) / (2.0f * attenuation.exponential);
And finally, here is my PointLightPass fragment shader:
#version 450 core
struct BaseLight
{
vec3 intensities;//a.k.a color of light
float ambientCoeff;
};
struct Attenuation
{
float constant;
float linear;
float exponential;
};
struct PointLight
{
BaseLight base;
Attenuation attenuation;
vec3 position;
};
struct Material
{
float shininess;
vec3 specularColor;
float ambientCoeff;
};
layout (std140) uniform Viewport
{
uniform mat4 Projection;
uniform mat4 View;
uniform mat4 ViewProjection;
uniform vec2 scrResolution;
};
layout(binding = 0) uniform sampler2D gPositionMap;
layout(binding = 1) uniform sampler2D gAlbedoMap;
layout(binding = 2) uniform sampler2D gNormalMap;
layout(binding = 3) uniform sampler2D gSpecularMap;
uniform vec3 cameraPosition;
uniform PointLight pointLight;
out vec4 fragmentColor;
vec2 FetchTexCoord()
{
return gl_FragCoord.xy / scrResolution;
}
void main()
{
vec2 texCoord = FetchTexCoord();
vec3 gPosition = texture(gPositionMap, texCoord).xyz;
vec3 gSurfaceColor = texture(gAlbedoMap, texCoord).xyz;
vec3 gNormal = texture(gNormalMap, texCoord).xyz;
vec3 gSpecColor = texture(gSpecularMap, texCoord).xyz;
float gSpecPower = texture(gSpecularMap, texCoord).a;
vec3 totalLight = gSurfaceColor * 0.1; //TODO remove hardcoded ambient light
vec3 viewDir = normalize(cameraPosition - gPosition);
vec3 lightDir = normalize(pointLight.position - gPosition);
vec3 diffuse = max(dot(gNormal, lightDir), 0.0f) * gSurfaceColor *
pointLight.base.intensities;
vec3 halfWayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(gNormal, halfWayDir), 0.0f), 1.0f);
vec3 specular = pointLight.base.intensities * spec /** gSpecColor*/;
float distance = length(pointLight.position - gPosition);
float attenuation = 1.0f / (1.0f + pointLight.attenuation.linear * distance
+ pointLight.attenuation.exponential * distance * distance +
pointLight.attenuation.constant);
diffuse *= attenuation;
specular *= attenuation;
totalLight += diffuse + specular;
fragmentColor = vec4(totalLight, 1.0f);
}
So what can you suggest to deal with this issue ?
EDIT : Here are more details :
For deferred shading,
I populate my GBuffer;
I make an ambient light pass where I render a fullscreen quad
with the ambient colors :
#version 420 core
layout (std140) uniform Viewport
{
uniform mat4 Projection;
uniform mat4 View;
uniform mat4 ViewProjection;
uniform vec2 scrResolution;
};
layout(binding = 1) uniform sampler2D gAlbedoMap;
out vec4 fragmentColor;
vec2 FetchTexCoord()
{
return gl_FragCoord.xy / scrResolution;
}
void main()
{
vec2 texCoord = FetchTexCoord();
vec3 gSurfaceColor = texture(gAlbedoMap, texCoord).xyz;
vec3 totalLight = gSurfaceColor * 1.2; //TODO remove hardcoded ambient light
fragmentColor = vec4(totalLight, 1.0f);
}
Then I pass my point lights (see code above);
The reason you're having this problem is that you're using a "light volume" (a fact that you didn't make entirely clear in this question, but was brought up in your other question).
You are using the normal light attenuation equation. Well, you'll notice that this equation does not magically stop at some arbitrary radius. It is defined for all distances from 0 to infinity.
The purpose of your light volume is to prevent lighting contributions beyond a certain distance. Well, if your light attenuation doesn't go to zero at that distance, then you're going to see a discontinuity at the edge of the light volume.
If you're going to use a light volume, you need to use a light attenuation equation that actually is guaranteed to reach zero at the edge of the volume. Or failing that, you should pick a radius for your volume such that the attenuated strength of the light is nearly zero. And your radius is too small for that.
Keep making your radius bigger until you can't tell it's there.