本文主要是介绍OpenGL 学习笔记 法线贴图,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
法线贴图
效果图
法线贴图的意义以及基本介绍可以看opengl官方网站
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/04%20Normal%20Mapping/
本此的代码立方体顶点数据来自
https://blog.csdn.net/u010385624/article/details/91994006
关于切线空间介绍的十分清楚。
法线贴图介绍
我们的场景中已经充满了多边形物体,其中每个都可能由成百上千平坦的三角形组成。我们以向三角形上附加纹理的方式来增加额外细节,提升真实感,隐藏多边形几何体是由无数三角形组成的事实。纹理确有助益,然而当你近看它们时,这个事实便隐藏不住了。现实中的物体表面并非是平坦的,而是表现出无数(凹凸不平的)细节。
例如,砖块的表面。砖块的表面非常粗糙,显然不是完全平坦的:它包含着接缝处水泥凹痕,以及非常多的细小的空洞。
切线空间
https://zhuanlan.zhihu.com/p/139593847
代码讲解
代码中渲染片段
while (!glfwWindowShouldClose(window)){// per-frame time logic// --------------------float currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;// input// -----processInput(window);// render// ------glClearColor(0.1f, 0.1f, 0.1f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// configure view/projection matricesglm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);glm::mat4 view = camera.GetViewMatrix();shader.use();shader.setMat4("projection", projection);shader.setMat4("view", view);// render normal-mapped quadglm::mat4 model = glm::mat4(1.0f);model = glm::rotate(model, glm::radians((float)glfwGetTime() * -10.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0))); // rotate the quad to show normal mapping from multiple directionsshader.setMat4("model", model);shader.setVec3("viewPos", camera.Position);shader.setVec3("lightPos", lightPos);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, diffuseMap);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, normalMap);renderCube();//renderQuad();// render light source (simply re-renders a smaller plane at the light's position for debugging/visualization)model = glm::mat4(1.0f);model = glm::translate(model, lightPos);model = glm::scale(model, glm::vec3(0.1f));shader.setMat4("model", model);renderCube();//renderQuad();// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)// -------------------------------------------------------------------------------glfwSwapBuffers(window);glfwPollEvents();}glfwTerminate();return 0;
}
可以看到都是正常的赋值,将所需的模型坐标填入。其中我们调用了renderCube()来进行绘制立方体,而且我们其中切线空间转换也在里面进行。
void renderCube()
{if(cubeVAO = 1){// set up vertex data (and buffer(s)) and configure vertex attributes// ------------------------------------------------------------------float vertices[] = {// positions // normals // texture coords-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f};//共有6个面12个三角形 36个顶点float tbnFloats[216]; // 36 个顶点的切线和副切线向量一共有 216 个浮点数for (int i = 0; i < 12; i++){int firstIndex = i * 24; // 三角形第 1 个顶点坐标起始索引int secondIndex = firstIndex + 8; // 三角形第 2 个顶点坐标起始索引int thirdIndex = secondIndex + 8; // 三角形第 3 个顶点坐标起始索引// 求得一个三角形的三个顶点坐标glm::vec3 pos1(vertices[firstIndex], vertices[firstIndex + 1], vertices[firstIndex + 2]);glm::vec3 pos2(vertices[secondIndex], vertices[secondIndex + 1], vertices[secondIndex + 2]);glm::vec3 pos3(vertices[thirdIndex], vertices[thirdIndex + 1], vertices[thirdIndex + 2]);// 求得一个三角形的三个顶点对应的 UV 坐标glm::vec2 uv1(vertices[firstIndex + 6], vertices[firstIndex + 7]);glm::vec2 uv2(vertices[secondIndex + 6], vertices[secondIndex + 7]);glm::vec2 uv3(vertices[thirdIndex + 6], vertices[thirdIndex + 7]);glm::vec3 edge1 = pos2 - pos1;glm::vec3 edge2 = pos3 - pos1;glm::vec2 deltaUV1 = uv2 - uv1;glm::vec2 deltaUV2 = uv3 - uv1;float f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y);glm::vec3 tangent;glm::vec3 bitTangent;tangent.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x);tangent.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y);tangent.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z);bitTangent.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x);bitTangent.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y);bitTangent.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z);bitTangent = glm::normalize(bitTangent);// 将每个三角形顶点的切线和副切线数据放到数组里int startTBNIndex = i * 18;tbnFloats[startTBNIndex + 0] = tangent.x;tbnFloats[startTBNIndex + 1] = tangent.y;tbnFloats[startTBNIndex + 2] = tangent.z;tbnFloats[startTBNIndex + 3] = bitTangent.x;tbnFloats[startTBNIndex + 4] = bitTangent.y;tbnFloats[startTBNIndex + 5] = bitTangent.z;tbnFloats[startTBNIndex + 6] = tangent.x;tbnFloats[startTBNIndex + 7] = tangent.y;tbnFloats[startTBNIndex + 8] = tangent.z;tbnFloats[startTBNIndex + 9] = bitTangent.x;tbnFloats[startTBNIndex + 10] = bitTangent.y;tbnFloats[startTBNIndex + 11] = bitTangent.z;tbnFloats[startTBNIndex + 12] = tangent.x;tbnFloats[startTBNIndex + 13] = tangent.y;tbnFloats[startTBNIndex + 14] = tangent.z;tbnFloats[startTBNIndex + 15] = bitTangent.x;tbnFloats[startTBNIndex + 16] = bitTangent.y;tbnFloats[startTBNIndex + 17] = bitTangent.z;}float finishVertices[504];// 一共 36 个顶点,按照特定顺序合并即可for (int i = 0; i < 36; ++i){int finishStartIndex = i * 14;int verticesStartIndex = i * 8;int tbnStartIndex = i * 6;finishVertices[finishStartIndex + 0] = vertices[verticesStartIndex + 0];finishVertices[finishStartIndex + 1] = vertices[verticesStartIndex + 1];finishVertices[finishStartIndex + 2] = vertices[verticesStartIndex + 2];finishVertices[finishStartIndex + 3] = vertices[verticesStartIndex + 3];finishVertices[finishStartIndex + 4] = vertices[verticesStartIndex + 4];finishVertices[finishStartIndex + 5] = vertices[verticesStartIndex + 5];finishVertices[finishStartIndex + 6] = vertices[verticesStartIndex + 6];finishVertices[finishStartIndex + 7] = vertices[verticesStartIndex + 7];finishVertices[finishStartIndex + 8] = tbnFloats[tbnStartIndex + 0];finishVertices[finishStartIndex + 9] = tbnFloats[tbnStartIndex + 1];finishVertices[finishStartIndex + 10] = tbnFloats[tbnStartIndex + 2];finishVertices[finishStartIndex + 11] = tbnFloats[tbnStartIndex + 3];finishVertices[finishStartIndex + 12] = tbnFloats[tbnStartIndex + 4];finishVertices[finishStartIndex + 13] = tbnFloats[tbnStartIndex + 5];}//unsigned int cubeVAO = 1;//unsigned int cubeVBO;// configure Cube VAOglGenVertexArrays(1, &cubeVAO);glGenBuffers(1, &cubeVBO);glBindVertexArray(cubeVAO);glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);glBufferData(GL_ARRAY_BUFFER, sizeof(finishVertices), &finishVertices, GL_STATIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)0);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(2);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(6 * sizeof(float)));glEnableVertexAttribArray(3);glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(8 * sizeof(float)));glEnableVertexAttribArray(4);glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void*)(11 * sizeof(float)));}glBindVertexArray(cubeVAO);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);
}
finishVertices数组包含了我们所有的顶点。
顶点数组处理方法:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
layout (location = 3) in vec3 aTangent;
layout (location = 4) in vec3 aBitangent;out VS_OUT {vec3 FragPos;vec2 TexCoords;vec3 TangentLightPos;vec3 TangentViewPos;vec3 TangentFragPos;
} vs_out;uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;uniform vec3 lightPos;
uniform vec3 viewPos;void main()
{vs_out.FragPos = vec3(model * vec4(aPos, 1.0)); vs_out.TexCoords = aTexCoords;mat3 normalMatrix = transpose(inverse(mat3(model)));vec3 T = normalize(normalMatrix * aTangent);vec3 N = normalize(normalMatrix * aNormal);T = normalize(T - dot(T, N) * N);vec3 B = cross(N, T);//叉乘,获得富且先向量//注意,这里我们使用transpose函数,而不是inverse函数。//正交矩阵(每个轴既是单位向量同时相互垂直)的一大属性是一个正交矩阵的置换矩阵与它的逆矩阵相等。//这个属性和重要因为逆矩阵的求得比求置换开销大;结果却是一样的。mat3 TBN = transpose(mat3(T, B, N)); vs_out.TangentLightPos = TBN * lightPos;vs_out.TangentViewPos = TBN * viewPos;vs_out.TangentFragPos = TBN * vs_out.FragPos;gl_Position = projection * view * model * vec4(aPos, 1.0);
}
inverse :反转矩阵
transpose:矩阵求转置矩阵
正交化处理并且输出给片段着色器
#version 330 core
out vec4 FragColor;in VS_OUT {vec3 FragPos;vec2 TexCoords;vec3 TangentLightPos;vec3 TangentViewPos;vec3 TangentFragPos;
} fs_in;uniform sampler2D diffuseMap;
uniform sampler2D normalMap;uniform vec3 lightPos;
uniform vec3 viewPos;void main()
{ // 处理获取到的法线(从法线贴图的RGB中提取) // obtain normal from normal map in range [0,1]vec3 normal = texture(normalMap, fs_in.TexCoords).rgb;// transform normal vector to range [-1,1]normal = normalize(normal * 2.0 - 1.0); // this normal is in tangent space// get diffuse colorvec3 color = texture(diffuseMap, fs_in.TexCoords).rgb;// ambientvec3 ambient = 0.1 * color;// diffusevec3 lightDir = normalize(fs_in.TangentLightPos - fs_in.TangentFragPos);float diff = max(dot(lightDir, normal), 0.0);vec3 diffuse = diff * color;// specularvec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);vec3 reflectDir = reflect(-lightDir, normal);vec3 halfwayDir = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);vec3 specular = vec3(0.2) * spec;FragColor = vec4(ambient + diffuse + specular, 1.0);
}
这篇关于OpenGL 学习笔记 法线贴图的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!