[GLSL] Heightmap Bump Mapping (Berechnung der TBN-Matrix)
-
Hallo zusammen,
ich habe ein kleines Problem beim rendern einer heightmap basierten Bumpmap. Das Hauptproblem liegt wahrscheinlich bei der korrekten Berechnung der TBN-Matrix und der Übergabe dieser. Zuerst der relevante Quellcode.
//Berechnung der TBN-Matrix in der CPU Vector3<GLfloat> v1(vertices[0], vertices[1], vertices[2]); Vector3<GLfloat> v2(vertices[3], vertices[4], vertices[5]); Vector3<GLfloat> v3(vertices[6], vertices[7], vertices[8]); Vector3<GLfloat> c1(textures[0], textures[1], textures[2]); Vector3<GLfloat> c2(textures[3], textures[4], textures[5]); Vector3<GLfloat> c3(textures[6], textures[7], textures[8]); Vector3<GLfloat> v21 = v2 - v1; Vector3<GLfloat> v31 = v3 - v1; Vector3<GLfloat> c21 = c2 - c1; Vector3<GLfloat> c31 = c3 - c1; GLfloat denominator = c21.getX() * c31.getY() - c31.getX() * c21.getY(); if(denominator == 0.0f) { tSpaceVector = Vector3<GLfloat>(1.0f, 0.0f, 0.0f); tSpaceBinormal = Vector3<GLfloat>(0.0f, 1.0f, 0.0f); tSpaceNormal = Vector3<GLfloat>(0.0f, 0.0f, 1.0f); } else { tSpaceVector = Vector3<GLfloat>(v21 * c31.getX() - v31 * c21.getX()) * (1.0f / denominator); tSpaceBinormal = Vector3<GLfloat>(v21 * -c31.getY() + v31 * c21.getY()) * (1.0f / denominator); tSpaceNormal = tSpaceVector.crossProduct(tSpaceBinormal); tSpaceVector.normalize(); tSpaceBinormal.normalize(); //tSpaceNormal.normalize(); }
// glActiveTexture(GL_TEXTURE0 + activeTex); //glUniform1i(glGetUniformLocation(material->getShader()->getId(), "hasBumpMap"), 1); glUniform1i(glGetUniformLocation(material->getShader()->getId(), "bumpSampler"), activeTex); glVertexAttrib3f(glGetAttribLocation(material->getShader()->getId(), "tangent"), tSpaceVector.getX(), tSpaceVector.getY(), tSpaceVector.getZ()); glVertexAttrib3f(glGetAttribLocation(material->getShader()->getId(), "binormal"), tSpaceBinormal.getX(), tSpaceBinormal.getY(), tSpaceBinormal.getZ());
//Vertex-Shader varying vec3 vertexPosition; varying vec3 vertexNormal; varying vec3 lightVector; attribute vec3 tangent; attribute vec3 binormal; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = gl_MultiTexCoord0; vertexPosition = vec3(gl_ModelViewMatrix * gl_Vertex); vertexNormal = normalize(gl_NormalMatrix * gl_Normal); lightVector = normalize(vec3(gl_LightSource[0].position) - vertexPosition); vec3 t = normalize(gl_NormalMatrix * tangent); vec3 b = cross(vertexNormal, t); mat3 matrix = mat3(t, b, vertexNormal); lightVector *= matrix; vertexPosition *= matrix; }
//Fragment-Shader uniform sampler2D diffuseSampler; uniform sampler2D bumpSampler; varying vec3 vertexPosition; varying vec3 vertexNormal; varying vec3 lightVector; varying mat3 matrix; void main() { vec3 eyeVector = normalize(vertexPosition); //Bumpmapping calculations ivec2 texSize = textureSize(bumpSampler, 0); float hmdx = texture2D(bumpSampler, vec2(gl_TexCoord[0]) + vec2(1.0/texSize.x, 0.0)).x - texture2D(bumpSampler, vec2(gl_TexCoord[0]) - vec2(1.0/texSize.x, 0.0)).x; float hmdy = texture2D(bumpSampler, vec2(gl_TexCoord[0]) + vec2(0.0, 1.0/texSize.y)).x - texture2D(bumpSampler, vec2(gl_TexCoord[0]) - vec2(0.0, 1.0/texSize.y)).x; vec3 hmNormal = normalize(cross(vec3(1.0, 0.0, hmdx), vec3(0.0, 1.0, hmdy))); vec4 ambientValue = gl_FrontMaterial.ambient * gl_LightSource[0].ambient; vec4 diffuseValue = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse * max(dot(hmNormal, normalize(lightVector)), 0.0); vec4 specularValue = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(max(dot(normalize(reflect(-lightVector, hmNormal)), -eyeVector), 0.0), gl_FrontMaterial.shininess); gl_FragColor = texture2D(diffuseSampler, gl_TexCoord[0].xy) * (ambientValue + diffuseValue + specularValue); }
Wenn ich nur den "tSpaceVector" an den Shader übergebe, dann wird mein Box halbwegs korrekt gerendert. D.h. das SpecularLighting und DiffuseLighting funktioniert nur auf zwei Seiten der Box. Wenn ich "tSpaceVector" und "tSpaceBinormal" übergebe dann funktioniert das DiffuseLighting perfekt, nur vom SpecularLight ist nichts zu sehen. Hier ist ein Video wo mans sehen kann. Ich hab schon auf unzähligen Seiten mir Tutorials bezüglich Bumpmapping angeguckt, die sich jedoch alle auf Normalmapping beziehen. Ich würde gerne trotzdem Bump Mapping per Heightmap realisieren.
Hier die Texturen:
http://www7.pic-upload.de/thumb/12.01.13/9tluctqy76nj.jpg
http://www7.pic-upload.de/thumb/12.01.13/sunbw687nm8d.jpghttp://www.youtube.com/watch?v=knOiHYDNKws
http://www.youtube.com/watch?v=EiLwTMhsSBw
-
aptem schrieb:
glVertexAttrib3f(glGetAttribLocation(material->getShader()->getId(), "tangent"), tSpaceVector.getX(), tSpaceVector.getY(), tSpaceVector.getZ()); glVertexAttrib3f(glGetAttribLocation(material->getShader()->getId(), "binormal"), tSpaceBinormal.getX(), tSpaceBinormal.getY(), tSpaceBinormal.getZ());
das ist doch ein wuerfel, oder? genau wie dieser pro vertex eine normale hat, so hat er auch eine tangente und bitangente.
ich empfehle dir dabei pro dreieck 3 vertices zu uebergeben, also sie nicht zwischen dreiecken zu sharen, weil der tangentspace aufgrund der richtung der UV coordinaten und positionen aufgebaut wird, kann also passieren dass ein vertex der normale, textcoord und position fuer angrenzende dreiecke teilt, der tangentspace anders ist. (spaeter kannst du das natuerlich wieder optimieren).
btw. super fehlerbericht mit screenshots und sogar youtube vid , selten sowas!
-
Also irgendwie bekomme ich das nicht hin. Bei dem unter abgebildeten Code funktioniert die Beleuchtung an sich, jedoch verläuft das Licht nicht flüssig, sondern klumpig, d.h. linke Seite hell, linke und mitte Hell, linke dunkel und mitte Hell, usw. also kein Schattenverlauf. Deswegen kann ich auch nicht sagen ob das diffuse Licht überhaupt korrekt berechnet wird, geschweige das meine Methodik zur Berechnung und Übergabe der TBN-Matrix korrekt ist. Vielleicht ist im Shader ein Bug? Ich weiß jetzt auch nicht mehr was ich probieren/machen kann um das Problem zu finden.
Ich habe jetzt ein Array in dem die Tangenten für jedes Dreieck gespeichert werden. (Beim Würfel -> 36 Vertices á 12 Tangenten)
Das Array übergebe ich dann per "glVertexAttribPointer()"
std::vector<GLfloat> tangentsVec; for(int i = 0;i < (indicesCount * 3) / 9;i++) { Vector3<GLfloat> v1(vertices[i*9+0], vertices[i*9+1], vertices[i*9+2]); Vector3<GLfloat> v2(vertices[i*9+3], vertices[i*9+4], vertices[i*9+5]); Vector3<GLfloat> v3(vertices[i*9+6], vertices[i*9+7], vertices[i*9+8]); Vector3<GLfloat> t1(textures[i*9+0], textures[i*9+1], textures[i*9+2]); Vector3<GLfloat> t2(textures[i*9+3], textures[i*9+4], textures[i*9+5]); Vector3<GLfloat> t3(textures[i*9+6], textures[i*9+7], textures[i*9+8]); Vector3<GLfloat> v31 = v3 - v1; Vector3<GLfloat> v21 = v2 - v1; Vector3<GLfloat> c31 = t3 - t1; Vector3<GLfloat> c21 = t2 - t1; GLfloat denominator = c21.getX() * c31.getY() - c31.getX() * c21.getY(); if(denominator == 0.0f) { tangentsVec.push_back(1.0f); tangentsVec.push_back(0.0f); tangentsVec.push_back(0.0f); } else { Vector3<GLfloat> tVec = Vector3<GLfloat>(v21 * c31.getY() - v31 * c21.getY()) * (1.0f / denominator); tVec.normalize(); tangentsVec.push_back(tVec.getX()); tangentsVec.push_back(tVec.getY()); tangentsVec.push_back(tVec.getZ()); } } tangents = new GLfloat[tangentsVec.size()]; for(int i = 0;i < tangentsVec.size();i++) { tangents[i] = tangentsVec[i]; }
//Vertex-Shader varying vec3 vertexPosition; varying vec3 vertexNormal; varying vec3 lightVector; attribute vec3 tangent; //attribute vec3 binormal; bool isCalculated = false; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = gl_MultiTexCoord0; vertexPosition = vec3(gl_ModelViewMatrix * gl_Vertex); vertexNormal = normalize(gl_NormalMatrix * gl_Normal); lightVector = normalize(vec3(gl_LightSource[0].position) - vertexPosition); vec3 t = normalize(gl_NormalMatrix * tangent); vec3 b = cross(t, vertexNormal); vec3 v; v.x = dot(lightVector, t); v.y = dot(lightVector, b); v.z = dot(lightVector, vertexNormal); lightVector = normalize(v); }
//Fragment-Shader uniform sampler2D diffuseSampler; uniform sampler2D bumpSampler; uniform bool hasDiffuseMap; uniform bool hasBumpMap; varying vec3 vertexPosition; varying vec3 vertexNormal; varying vec3 lightVector; //varying mat3 matrix; void main() { vec3 eyeVector = normalize(vertexPosition + vec3(0.0, 0.0, texture2D(bumpSampler, vec2(gl_TexCoord[0])).x)); //Bumpmapping calculations ivec2 texSize = textureSize(bumpSampler, 0); float hmdx = texture2D(bumpSampler, vec2(gl_TexCoord[0]) + vec2(1.0/texSize.x, 0.0)).x - texture2D(bumpSampler, vec2(gl_TexCoord[0]) - vec2(1.0/texSize.x, 0.0)).x; float hmdy = texture2D(bumpSampler, vec2(gl_TexCoord[0]) + vec2(0.0, 1.0/texSize.y)).x - texture2D(bumpSampler, vec2(gl_TexCoord[0]) - vec2(0.0, 1.0/texSize.y)).x; vec3 hmNormal = normalize(cross(vec3(1.0, 0.0, hmdx), vec3(0.0, 1.0, hmdy))); vec4 ambientValue = gl_FrontMaterial.ambient * gl_LightSource[0].ambient; vec4 diffuseValue = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse * max(dot(lightVector, hmNormal), 0.0); vec4 specularValue = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(max(dot(normalize(reflect(-lightVector, hmNormal)), -eyeVector), 0.0), gl_FrontMaterial.shininess); //gl_FragColor = texture2D(diffuseSampler, vec2(gl_TexCoord[0])) * (ambientValue + diffuseValue + specularValue); gl_FragColor = texture2D(diffuseSampler, gl_TexCoord[0].xy) * (ambientValue + diffuseValue); }