I have been trying to implement a heightmap to my terrain shader, but the terrain remains flat. The texture is properly loaded in the vertex shader, and I try to use the greyscale values of the texture based on the mesh's uvs to adjust the vertex height:
//DIFFUSE VERTEX SHADER
#version 330
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
in vec3 vertex;
in vec3 normal;
in vec2 uv;
uniform sampler2D heightmap;
out vec2 texCoord;
void main( void ){
vec3 _vertex = vertex;
_vertex.y = texture(heightmap, uv).r * 2.f;
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(_vertex, 1.f);
texCoord = uv;
}
Fragment: (the splatmap works so ignore that)
uniform sampler2D splatmap;
uniform sampler2D diffuse1;
uniform sampler2D diffuse2;
uniform sampler2D diffuse3;
uniform sampler2D diffuse4;
in vec2 texCoord;
out vec4 fragment_color;
void main( void ) {
///Loading the splatmap and the diffuse textures
vec4 splatTexture = texture2D(splatmap, texCoord);
vec4 diffuseTexture1 = texture2D(diffuse1, texCoord);
vec4 diffuseTexture2 = texture2D(diffuse2, texCoord);
vec4 diffuseTexture3 = texture2D(diffuse3, texCoord);
vec4 diffuseTexture4 = texture2D(diffuse4, texCoord);
//Interpolate between the different textures using the splatmap's rgb values (works)
diffuseTexture1 *= splatTexture.r;
diffuseTexture2 = mix (diffuseTexture1, diffuseTexture2, splatTexture.g);
diffuseTexture3 = mix (diffuseTexture2,diffuseTexture3, splatTexture.b);
vec4 outcolor = mix (diffuseTexture3, diffuseTexture4, splatTexture.a);
fragment_color = outcolor;
}
Some additional info:
All textures are loaded like this in my terrain material and passed to the shader (works properly):
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, heightMap->getId());
glUniform1i (_shader->getUniformLocation("heightMap"),0);
...
The plane mesh uvs are mapped like this:
(0,1) (1,1)
(0,0) (1,0)
I guess I am doing something horribly wrong, but I can't figure out what. Any help is appreciated!
Does your writing this:
The plane mesh uvs are mapped like this:
(0,1) (1,1)
(0,0) (1,0)
… mean that your mesh consists of just 4 vertices? If so, then that's your problem right there: The Vertex shader can not magically create "new" vertices, so your heightmap texture is sampled at only 4 points (and nothing in between).
And because you sample the texture coordinates at integer values and your texture coordinates and are at 0 and 1, you're effectively sampling the very same texture coordinate, so you're going to see the same displacement for all four vertices.
Solution: Tesselate your base mesh so that there are actually vertices available to displace. A tesselation shader is perfectly fine for that.
EDIT:
BTW, you can simplyfiy your vertex shader a bit: For the attributes make it a
in vec2 vertex;
which requires just 2/3 of the space of vec3, since you're not using the z component anyway.
float y = texture(heightmap, uv).r * 2.f;
gl_Position =
projectionMatrix
* viewMatrix
* modelMatrix
* vec4(vertex.x, y, vertex.y, 1.f);
Related
I'm doing per-pixel lighting(phong shading) on my terrain. I'm using a heightmap to generate the terrain height and then calculating the normal for each vertex. The normals are interpolated in the fragment shader and also normalized.
I am getting some weird dark lines near the edges of triangles where there shouldn't be.
http://imgur.com/L2kj4ca
I checked if the normals were correct using a geometry shader to draw the normals on the terrain and they seem to be correct.
http://imgur.com/FrJpdXI
There is no point using a normal map for the terrain it will just give pretty much the same normals. The problem lies with the way the normals are interpolated across a triangle.
I am out of idea's how to solve this. I couldn't find any working solution online.
Terrain Vertex Shader:
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 textureCoords;
out vec2 pass_textureCoords;
out vec3 surfaceNormal;
out vec3 toLightVector;
out float visibility;
uniform mat4 transformationMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 lightPosition;
const float density = 0.0035;
const float gradient = 5.0;
void main()
{
vec4 worldPosition = transformationMatrix * vec4(position, 1.0f);
vec4 positionRelativeToCam = viewMatrix * worldPosition;
gl_Position = projectionMatrix * positionRelativeToCam;
pass_textureCoords = textureCoords;
surfaceNormal = (transformationMatrix * vec4(normal, 0.0f)).xyz;
toLightVector = lightPosition - worldPosition.xyz;
float distance = length(positionRelativeToCam.xyz);
visibility = exp(-pow((distance * density), gradient));
visibility = clamp(visibility, 0.0, 1.0);
}
Terrain Fragment Shader:
#version 330 core
in vec2 pass_textureCoords;
in vec3 surfaceNormal;
in vec3 toLightVector;
in float visibility;
out vec4 colour;
uniform vec3 lightColour;
uniform vec3 fogColour;
uniform sampler2DArray blendMap;
uniform sampler2DArray diffuseMap;
void main()
{
vec4 blendMapColour = texture(blendMap, vec3(pass_textureCoords, 0));
float backTextureAmount = 1 - (blendMapColour.r + blendMapColour.g + blendMapColour.b);
vec2 tiledCoords = pass_textureCoords * 255.0;
vec4 backgroundTextureColour = texture(diffuseMap, vec3(tiledCoords, 0)) * backTextureAmount;
vec4 rTextureColour = texture(diffuseMap, vec3(tiledCoords, 1)) * blendMapColour.r;
vec4 gTextureColour = texture(diffuseMap, vec3(tiledCoords, 2)) * blendMapColour.g;
vec4 bTextureColour = texture(diffuseMap, vec3(tiledCoords, 3)) * blendMapColour.b;
vec4 diffuseColour = backgroundTextureColour + rTextureColour + gTextureColour + bTextureColour;
vec3 unitSurfaceNormal = normalize(surfaceNormal);
vec3 unitToLightVector = normalize(toLightVector);
float brightness = dot(unitSurfaceNormal, unitToLightVector);
float ambient = 0.2;
brightness = max(brightness, ambient);
vec3 diffuse = brightness * lightColour;
colour = vec4(diffuse, 1.0) * diffuseColour;
colour = mix(vec4(fogColour, 1.0), colour, visibility);
}
This can be either two issues :
1. Incorrect normals :
There is different types of shading : Flat shading, Gouraud shading and Phong shading (different of Phong specular) example :
You usually want to do a Phong shading. To do that, OpenGL make your life easier and interpolate for you the normals between each vertex of each triangle, so at each pixel you have the correct normal for this point: but you still need to feed it proper normal values, that are the average of the normals of every triangles attached to this vertex. So in your function that create the vertex, the normals and the UVs, you need to compute the normal at each vertex by averaging every triangle normal attached to this vertex. illustration
2. Subdivision problem :
The other possible issue is that your terrain is not subdivided enough, or your heightmap resolution is too low, resulting to this kind of glitch because of the difference of height between two vertex in one triangle (so between two pixels in your heightmap).
Maybe if you can provide some of your code and shaders, maybe even the heightmap so we can pin exactly what is happening in your case.
This is old, but I suspect you're not transforming your normal using the transposed inverse of the upper 3x3 part of your modelview matrix. See this. Not sure what's in "transformationMatrix", but if you're using it to transform the vertex and the normal something is probably fishy...
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 :)
I am currently trying to make a little game in OpenGL as an attempt to learn how to use the API. I've come to a point where I can move a camera around a simple scene, and I can render models and shade them with a simple phong model shader.
I'm right now working on texturing the models in the scene, so I got a copy of Maya and made (with quite some struggle) a square with a texture with the UV mapping made in within Maya.
When I render the scene, the texture is applied, but far from correct. I read the models as .obj files with a parser I wrote myself, and the textures are read using a funtion I found online a while back.
I'm not sure how to describe the problem in sufficient detail, nor what to look for in the code, but here are some code fractions that I would suspect contained the problem.
Reading the texture
GLuint loadTexture(Image* image){
GLuint textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB,
image->width, image->height,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
image->pixels);
return textureId;
}
Setting the texture prior to rendering the mesh
// set texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, this->body_texture);
current_shader->setUniformint(0, "Difuse_texture");
Vertex shader
#version 410
layout(location = 0) in vec3 VertexPosition;
layout(location = 1) in vec3 VertexNormal;
layout(location = 1) in vec2 TextureCoord;
out vec3 Position;
out vec3 Normal;
out vec2 TexCoord;
uniform mat4 ModelMatrix;
uniform mat4 VeiwMatrix;
uniform mat4 ProjectionMatrix;
uniform mat3 NormalMatrix;
void main(){
mat4 ModelVeiwMatrix = VeiwMatrix * ModelMatrix;
mat4 MVP = ProjectionMatrix * ModelVeiwMatrix;
TexCoord = TextureCoord;
Normal = normalize( NormalMatrix * VertexNormal );
Position = vec3(ModelVeiwMatrix * vec4(VertexPosition, 1.0));
gl_Position = MVP * vec4(VertexPosition, 1.0);
}
Fragment shader
#version 410
in vec3 Position;
in vec3 Normal;
in vec2 TexCoord;
uniform vec4 LightPosition;
uniform vec3 LightIntensity;
uniform vec3 Kd;
uniform vec3 Ka;
uniform vec3 Ks;
uniform float Shininess;
uniform sampler2D Difuse_texture;
layout(location = 0) out vec4 FragColor;
vec4 ads(){
vec3 n = normalize( Normal );
vec3 s = normalize( vec3(LightPosition) - Position );
vec3 v = normalize( vec3(-Position) );
vec3 r = reflect( -s, n );
vec3 specular_light = Ks * pow(max(dot(r, v), 0.0), Shininess);
vec3 ad_light = Ka + Kd * max(dot(s, n), 0.0);
vec4 TexColor = texture2D(Difuse_texture, TexCoord);
return TexColor; // (vec4(LightIntensity, 1.0) * (vec4(ad_light, 1.0) * TexColor + vec4(specular_light, 1.0)));
}
void main() {
FragColor = ads();
}
I know some things are written strangely, but at this point I'm starting to just try anything to get it working.
Does anyone have a suggestion on how to solve this strange UV mapping?
EDIT:
OBJ LOADING
I have made the obj loader print all vertex attributes and compared these with the indexing in the .obj file. It looks like the verecies, normals and UVs are showing in the correct order.
Screenshot
The scene looks like this using just simple reg to green gradient as trexture image.
(The square should by my understading show the gradient from the texture? not just a single color)
Alignment sounds like a possible flaw, how can I correct this?
a http://imageshack.com/a/img674/9927/y0bJ51.png
SOLUTION
I made a very simple and easy to overlook mistake. In the top of the vertex shader i wrote
layout(location = 0) in vec3 VertexPosition;
layout(location = 1) in vec3 VertexNormal;
layout(location = 1) in vec2 TextureCoord;
So I guess that when I sent the normal data to location 1, I set the Texture coordinates to normal data, so the UV coords never reached the fragment shader.
Changeing to the folowing resolved the problem without further change.
layout(location = 0) in vec3 VertexPosition;
layout(location = 1) in vec3 VertexNormal;
layout(location = 2) in vec2 TextureCoord;
I've successfully rendered my scene from my light's point of view onto a depth cubemap, but I don't quite understand how I can actually project it onto my scene.
Here's a short clip of the current situation: http://youtu.be/54WXDWxqmXw
I found an implementation example on how to do it over here:
http://www.opengl.org/discussion_boards/showthread.php/174093-GLSL-cube-shadows-projecting?p=1219162&viewfull=1#post1219162
It seemed fairly easy to understand, so I figured this would be a great way to start off with, but I'm having some difficulties with the matrices (As shown in the video above).
My Vertex Shader:
#version 330 core
layout(std140) uniform ViewProjection
{
mat4 V;
mat4 P;
};
layout(location = 0) in vec3 vertexPosition;
layout(location = 1) in vec2 vertexUV;
out vec2 UV;
out vec4 posCs;
uniform mat4 M;
uniform mat4 lightView;
void main()
{
mat4 MVP = P *V *M;
gl_Position = MVP *vec4(vertexPosition,1);
UV = vertexUV;
posCs = V *M *vec4(vertexPosition,1);
}
Fragment Shader:
#version 330 core
in vec2 UV;
in vec4 posCs;
out vec4 color;
// Diffuse texture
uniform sampler2D renderTexture;
uniform samplerCubeShadow shadowCubeMap;
uniform mat4 lightView;
uniform mat4 lightProjection;
uniform mat4 camViewInv;
void main()
{
color = texture2D(renderTexture,UV).rgba;
mat4 lView = mat4(1); // The light is currently at the world origin, so we'll skip the transformation for now (The less potential error sources the better)
vec4 posLs = lView *camViewInv *posCs;
vec4 posAbs = abs(posLs);
float fs_z = -max(posAbs.x,max(posAbs.y,posAbs.z));
vec4 clip = lightProjection *vec4(0.0,0.0,fs_z,1.0);
float depth = (clip.z /clip.w) *0.5 +0.5;
vec4 r = shadowCube(shadowCubeMap,vec4(posLs.xyz,depth));
color *= r;
}
(I've only posted the relevant parts)
lightProjection is the same projection matrix that I've used to render the scene into the cubemap.
I'm not entirely sure about 'camViewInv', from the example I've linked above I came up with this:
glm::mat4 camViewInv(
camView[0][0],camView[1][0],camView[2][0],0.0f,
camView[0][1],camView[1][1],camView[2][1],0.0f,
camView[0][2],camView[1][2],camView[2][2],0.0f,
camPos[0],camPos[1],camPos[2],1.0f
);
camView being the camera's view matrix, and camPos the camera's worldspace position.
Everything else should be self-explanatory I believe.
I can't see anything wrong with the shaders, but I'm fairly certain the scene is rendered correctly to the cubemap (As shown in the video above). Maybe someone more versed than me can spot the issue.
// Update:
Some additional information about the creation / usage of the shadow cubemap:
Creating the cubemap texture:
unsigned int frameBuffer;
glGenFramebuffers(1,&frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER,frameBuffer);
unsigned int texture;
glGenTextures(1,&texture);
glBindTexture(GL_TEXTURE_CUBE_MAP,texture);
glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL);
glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_R,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_COMPARE_MODE,GL_COMPARE_R_TO_TEXTURE);
for(int i=0;i<6;i++)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X +i,0,GL_DEPTH_COMPONENT,size,size,0,GL_DEPTH_COMPONENT,GL_FLOAT,0);
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_CUBE_MAP_POSITIVE_X +i,texture,0);
glDrawBuffer(GL_NONE);
}
The light's matrices:
glm::perspective<float>(90.f,1.f,2.f,m_distance); // Projection Matrix
// View Matrices
glm::vec3 pos = GetPosition(); // Light worldspace position
glm::lookAt(pos,pos +glm::vec3(1,0,0),glm::vec3(0,1,0));
glm::lookAt(pos,pos +glm::vec3(-1,0,0),glm::vec3(0,1,0));
glm::lookAt(pos,pos +glm::vec3(0,1,0),glm::vec3(0,0,-1))
glm::lookAt(pos,pos +glm::vec3(0,-1,0),glm::vec3(0,0,1))
glm::lookAt(pos,pos +glm::vec3(0,0,1),glm::vec3(0,1,0))
glm::lookAt(pos,pos +glm::vec3(0,0,-1),glm::vec3(0,1,0))
Vertex Shader:
#version 330 core
layout(location = 0) in vec4 vertexPosition;
uniform mat4 shadowMVP;
void main()
{
gl_Position = shadowMVP *vertexPosition;
}
Fragment Shader:
#version 330 core
layout(location = 0) out float fragmentDepth;
void main()
{
fragmentdepth = gl_FragCoord.z;
}
I would suggest doing this in world space, light positions are typically defined in world space and it will reduce the workload if you keep it that way. I removed a bunch of uniforms that you do not need if you do this in world space.
Compute lighting direction and depth in vtx. shader:
#version 330 core
layout(std140) uniform ViewProjection
{
mat4 V;
mat4 P;
};
layout(location = 0) in vec4 vertexPosition; // W is automatically assigned 1, if missing.
layout(location = 1) in vec2 vertexUV;
out vec2 UV;
out vec4 lightDirDepth; // Direction = xyz, Depth = w
uniform mat4 M;
uniform vec3 lightPos; // World Space Light Pos
uniform vec2 shadowZRange; // Near / Far clip plane distances for shadow's camera
float vecToDepth (vec3 Vec)
{
vec3 AbsVec = abs (Vec);
float LocalZcomp = max (AbsVec.x, max (AbsVec.y, AbsVec.z));
const float n = shadowZRange [0]; // Near plane when the shadow map was built
const float f = shadowZRange [1]; // Far plane when the shadow map was built
float NormZComp = (f+n) / (f-n) - (2.0*f*n)/(f-n)/LocalZcomp;
return (NormZComp + 1.0) * 0.5;
}
void main()
{
mat4 MVP = P *V *M;
gl_Position = MVP *vertexPosition;
UV = vertexUV;
vec3 lightDir = lightPos - (M *vertexPosition).xyz;
float lightDepth = vecToDepth (lightDir);
lightDirDepth = vec4 (lightDir, lightDepth);
}
Modified Fragment Shader (sample cubemap using light dir, and test against depth):
#version 330 core
in vec2 UV;
in vec4 lightDirDepth; // Direction = xyz, Depth = w
out vec4 color;
// Diffuse texture
uniform sampler2D renderTexture;
uniform samplerCubeShadow shadowCubeMap;
void main()
{
const float bias = 0.0001; // Prevent shadow acne
color = texture (renderTexture,UV).rgba;
float r = texture (shadowCubeMap, vec4 (lightDirDepth.xyz, lightDirDepth.w + bias));
color *= r;
}
I added two new uniforms:
lightPos -- World space position of your light
shadowZRange -- The values of your near and far plane when you built your shadow cube map, packed into a vec2
Let me know if you need me to explain anything or if this does not produce meaningful results.
How to scrolling a texture on a plane?
So I have a plane with a texture, can I use a shader to scroll left from right (infinite) the texture on it?
Setup the texture wrapping mode using
glTexParameteri(TextureID, L_TEXTURE_WRAP_S, GL_REPEAT)
Add the float uniform named Time to your texturing shader
Use something like texture2D(sampler, u + Time, v) while fetching texture sample.
Update the Time uniform using some timer in your code.
Here's a GLSL shader:
/*VERTEX_PROGRAM*/
in vec4 in_Vertex;
in vec4 in_TexCoord;
uniform mat4 ModelViewMatrix;
uniform mat4 ProjectionMatrix;
out vec2 TexCoord;
void main()
{
gl_Position = ProjectionMatrix * ModelViewMatrix * in_Vertex;
TexCoord = vec2( in_TexCoord );
}
/*FRAGMENT_PROGRAM*/
in vec2 TexCoord;
uniform sampler2D Texture0;
/// Updated in external code
uniform float Time;
out vec4 out_FragColor;
void main()
{
/// "u" coordinate is altered
out_FragColor = texture( Texture0, vec2(TexCoord.x + Time, TexCoord.y) );
}