openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节

openGL系列文章目录

文章目录

  • openGL系列文章目录
  • 前言
  • 一、法线贴图?
  • 二、代码
    • 1.主程序
    • 2.着色器程序
  • 运行效果
  • 源码下载

前言

凹凸贴图的一种替代方法是使用查找表来替换法向量。这样我们就可以在不依赖数学函
数的情况下,对凸起进行构造,例如月球上的陨石坑所对应的凸起。一种使用查找表的常
见方法叫作法线贴图。
为了理解法线贴图的工作原理,我们首先注意,向量通过3 字节存储,X、Y 和Z 分量
各占1 字节,就可以达到合理的精度。这样,我们就可以将法向量存储在彩色图像文件中,
其中R、G 和B 分量分别对应于X、Y 和Z。图像中的RGB 值以字节存储,通常被解释为[0…1]
范围内的值,但是向量可以有正负值分量。如果我们将法向量分量限制在[−1…+1]范围内,
那么在图像文件中将法向量N 存储为像素的简单转换是:

openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节_第1张图片
法线贴图使用一个图像文件(称为法线贴图),该图像文件包含在光照下所期望表面外
观的法向量。在法线贴图中,向量相对于任意平面XY 表示,其X 和Y 分量表示与“垂直”
的偏差,其Z 分量设置为1,严格垂直于XY 平面的向量(即没有偏差)将表示为(0, 0, 1),
而不垂直的向量将具有非零的X 和/或Y 分量。我们需要使用上面的公式将值转换至RGB 空
间;例如,(0, 0, 1)将存储为(0.5, 0.5, 1),因为实际偏移的范围为[−1…+1],而RGB 值的
范围为[0…1]。我们可以通过纹理单元的另一种妙用来生成这样一幅法线贴图:我们在纹理单元中存储
所需的法向量而非颜色。然后,在给定片段中,我们就可以使用采样器从法线贴图中查找值,
接下来,我们将所得的值作为法向量,而非输出像素颜色(在纹理贴图中我们是这么做的)。
图1 展示了一个法线贴图图像文件的例子,通过将GIMP 法线贴图插件[GI16]应用于
Luna [LU16]纹理而生成。法线贴图图像文件并不适合作为图像查看,我们展示这幅图就是为了
指明这一点,法线贴图最终看起来基本都是蓝色的。这是因为图像文件中每个像素的B 值(蓝色值)都是1(最大蓝色值),这会让它在作为图像时看起来是“蓝色的”。
图3 展示了两个不同的法线贴图图像文件(它们都由Luna [LU16]的纹理构建)以及在
Blinn-Phong 光照模型下将它们应用于球体的结果。从法线贴图查找到的法向量不能直接使用,因为它们是相对于上述的任意XY 平面定义
的,并没有考虑它们在物体上的位置以及在相机空间中的方向。这个问题的解决策略是建
立一个转换矩阵,用于将法向量转换为相机空间,如下所示。
在对象的每个顶点处,我们考虑与对象相切的平面。顶点处的物体的法向量垂直于该切
面。我们在该切面中定义两个相互垂直的向量,同时也垂直于法向量,称为切向量和副切
向量(有时称为副法向量)。构造我们期望的变换矩阵要求我们的模型包括每个顶点的切向
量(可以通过计算切向量和法向量的叉积来构建副切向量)。如果模型中没有定义切向量,
则需要通过计算得到它们。在球体的情况下,可以通过计算得到精确的切向量。

图1

一、法线贴图?

对于那些表面不可导以至于无法精确求解切向量的模型,其切向量可以通过近似得到,
例如在构造(或加载)模型时,将每个顶点指向下一个顶点的向量作为切向量。请注意,这种近似可能会导致切向量与顶点法向量不严格垂直。因此,如果要实现适用于各种模型
的法线贴图,需要考虑这种可能性(我们的解决方案中对此进行了处理)。
切向量与顶点、纹理坐标以及法向量一样,是从缓冲区(VBO)传递到顶点着色器中的
顶点属性。然后,顶点着色器通过应用MV 矩阵的逆转置并将结果沿着流水线转发以由光
栅器进行插值并最终进入片段着色器,从而对正常向量进行处理。逆转置的应用将法向量
和切向量转换为相机空间,之后我们使用叉积构造副切向量。一旦我们在相机空间中得到法向量、切向量和副切向量,就可以使用它们来构造矩阵(依其分量命名为“TBN”矩阵),该矩阵用于将从法线贴图中检索到的法向量转换为在相机空间中相对于物体表面的法向量。在片段着色器中,新法向量的计算在calcNewNormal()函数中完成。函数的第三行[包含dot(tangent,normal)]的计算确保切向量垂直于法向量。新的切向量和法向量的叉积就是副切向量。然后,我们创建一个类型为mat3 的3×3 矩阵,作为TBN。mat3 构造函数接收3 个向量作为参数,生成一个矩阵,其中顶行是第一个向量,中间行是第二个向量,底行是第三个向量(类似于从摄像机位置构建视图矩阵,见图2)。着色器使用片段的纹理坐标来提取与当前片段对应的法线贴图单元。着色器在提取时使用采样器变量“normMap”,并被绑定到纹理单元0(注意:因此在C++ / OpenGL 应用程序中必须将法线贴图图像附加到纹理单元0)。因为需要将颜色分量从纹理中存储范围[0…1]转换为其原始范围[−1 … + 1],我们将其乘以2.0 再减去1.0。
然后将TBN 矩阵应用于所得法向量以产生当前像素的最终法向量。着色器的其余部分与用于Phong 光的片段着色器相同。片段着色器代码基于Etay Meiri [ME11]的版本,制作法线贴图图像可以使用各种各样的工具。有的图像编辑工具就有制作法线贴图的功能,例如GIMP [GI16]和Photoshop [PH16]。它们通过分析图像中的边缘,推断凸起和凹陷,并产生相应的法线贴图。图4 显示了由Hastings-Trew [HT16]基于NASA 卫星数据创建的月面纹理图。其相应的法线贴图由GIMP 法线贴图插件[GP16],通过处理由Hastings-Trew 创建的黑白版本月面纹理。
图5 展示了使用两种不同方式渲染的,用以表现月球表面的球体。图5 左图中,
球体使用了原始的纹理贴图;图10.6 右图中,球体使用法线贴图的图像作为纹理(供参考)。它们都没有应用法线贴图。虽然左侧使用了纹理的“月球”非常逼真,但仔细观察
可以发现,纹理图案很明显拍摄于阳光从左侧照亮月球的时候,因为其山脊的阴影投射到
了右侧(在底部中心附近的火山口中最明显)。如果我们使用Phong 着色为此场景添加光
照,然后移动月球、相机或灯光来给场景添加动画,就会发现月球上的阴影不会如我们期
望地改变。此外,随着光源的移动(或相机移动),期望中会在山脊上出现许多镜面高光。但
是图5 左图使用了标准纹理的球体将只产生一个镜面高光,对应于光滑球体上所出
现的高光,这看起来非常不现实。配合法线贴图可以显著提高这类对象在光照下的真实感。
openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节_第2张图片
图2
openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节_第3张图片
图3
openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节_第4张图片
图4
openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节_第5张图片
图5
当我们在球体上使用法线贴图(而不是纹理)时,我们会得到图6 所示的结果。尽管
它不像标准纹理那么真实(现在),但是现在它确实响应了光照变化。图6的第一张图像
中从左侧进行光照,第二张图像中则从右侧进行光照。请注意蓝色和黄色箭头所示部分展
示了山脊周围漫反射光的变化以及镜面反射高光的移动。
openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节_第6张图片

图7 展示了在使用Phong 光照模型的情况下,将法线贴图与标准纹理相结合的效果。
月球的图像通过漫射区域进行了增强,镜面高光区域也会响应光源的移动(或相机或物体
移动)。两个图像中的光照分别来自左侧和右侧。
openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节_第7张图片
我们的程序现在需要两个纹理—— 一个用于月球表面图像,一个用于法线贴图——因此
需要有两个采样器。

二、代码

1.主程序

#include "glew/glew.h"
#include "glfw/glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "camera.h"
#include "Utils.h"
#include "Sphere.h"
#include "SOIL2/SOIL2.h"
#include 
#include 
#include 

using namespace std;

static const int screenWidth = 1920;
static const int screenHeight = 1080;

static const float pai = 3.1415926f;
float toRadians(float degrees) { return degrees * 2.f * pai / (float)360.f; }

static const int numVAOs = 1;
static const int numVBOs = 4;

float cameraX = 0, cameraY = 0, cameraZ = 0;
float sphereX = 0, sphereY = 0, sphereZ = 0;
float lightLocX = 0.f, lightLocY = 0.f, lightLocZ = 0.f;	//全局光照光源位置

GLuint renderingProgram = 1;
GLuint vao[numVAOs] = { 0 };
GLuint vbo[numVBOs] = { 0 };

// variable allocation for display
GLuint mvLoc = 0, projLoc = 0, nLoc = 0;
//phong 光照
GLuint globalAmbLoc = 0, ambLoc = 0, diffLoc = 0, specLoc = 0, posLoc = 0, mAmbLoc = 0, mDiffLoc = 0, mSpecLoc = 0, mShinLoc = 0;
int width = 0, height = 0;
float aspect = 0.f;
//MVP 矩阵
glm::mat4 mMat(1.f), vMat(1.f), pMat(1.f), mvMat(1.f), invTrMat(1.f);
glm::vec3 currentLightPos(0.f, 0.f, 0.f);

float lightPos[3] = { 0.f };
float rotAmt = -2.5f;

Sphere mySphere(48);
int numSphereVertices = 0;
int numSphereIndices = 0;

GLuint moonNormalMap = 0;
GLuint moonTexture = 0;

// white light
float globalAmbient[4] = { 0.1f, 0.1f, 0.1f, 1.f };
float lightAmbient[4] = { 0.f, 0.f, 0.f, 1.f };
float lightDiffuse[4] = { 1.f, 1.f, 1.f, 1.f };
float lightSpecular[4] = { 1.f, 1.f, 1.f, 1.f };

// silver material
float* matAmb = Utils::silverAmbient();
float* matDiff = Utils::silverDiffuse();
float* matSpec = Utils::silverSpecular();
float matShin = Utils::silverShininess();

Camera camera(glm::vec3(0.f, 0.f, 3.f));
//float cameraX = 0.f, cameraY = 0.f, cameraZ = 5.f;
GLboolean keys[1024] = { GL_FALSE };
GLboolean b_firstMouse = GL_TRUE;
float deltaTime = 0.f;

float lastFrame = 0.f;
float lastLocX = 0.f;
float lastLocY = 0.f;

void do_movement()
{
	if (keys[GLFW_KEY_W])
	{
		camera.ProcessKeyboard(FORWARD, deltaTime);
	}
	if (keys[GLFW_KEY_S])
	{
		camera.ProcessKeyboard(BACKWARD, deltaTime);
	}
	if (keys[GLFW_KEY_A])
	{
		camera.ProcessKeyboard(LEFT, deltaTime);
	}
	if (keys[GLFW_KEY_D])
	{
		camera.ProcessKeyboard(RIGHT, deltaTime);
	}
	/*if (keys[GLFW_KEY_ESCAPE])
	{
		glfwSetWindowShouldClose(window, GL_TRUE);
	}*/
}

void key_press_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	if ((key == GLFW_KEY_ESCAPE) && (action == GLFW_PRESS))
	{
		glfwSetWindowShouldClose(window, GL_TRUE);
	}
	if (action == GLFW_PRESS)
	{
		keys[key] = GLFW_TRUE;  //这里一定一定不能写成“==“,否则  按键WSAD按键失效!!!!!!!
	}
	else if (action == GLFW_RELEASE)
	{
		keys[key] = GLFW_FALSE;    //这里一定一定不能写成“==“,否则  按键WSAD按键失效!!!!!!!
	}
}

void mouse_move_callback(GLFWwindow* window, double xPos, double yPos)
{
	if (b_firstMouse)
	{
		lastLocX = xPos;
		lastLocY = yPos;
		b_firstMouse = GL_FALSE;
	}

	float xOffset = xPos - lastLocX;
	float yOffset = lastLocY - yPos;
	lastLocX = xPos;
	lastLocY = yPos;

	camera.ProcessMouseMovement(xOffset, yOffset);

}

void mouse_scroll_callback(GLFWwindow* window, double xPos, double yPos)
{
	camera.ProcessMouseScroll(yPos);
}

void setupVertices(void)
{
	numSphereIndices = mySphere.getNumIndices();

	vector<int> ind = mySphere.getIndices();

	vector<glm::vec3> vert = mySphere.getVertices();
	vector<glm::vec2> text = mySphere.getTexCoords();
	vector<glm::vec3> norm = mySphere.getNormals();
	vector<glm::vec3> tang = mySphere.getTangents();

	vector<float> pValues;
	vector<float> tValues;
	vector<float> nValues;
	vector<float> tanValues;

	numSphereVertices = mySphere.getNumVertices();

	for (int i = 0; i < mySphere.getNumIndices(); i++)
	{
		pValues.push_back(vert[ind[i]].x);  //gl_element_array_buffer,顶点对应成索引
		pValues.push_back(vert[ind[i]].y);
		pValues.push_back(vert[ind[i]].z);

		tValues.push_back(text[ind[i]].s);
		tValues.push_back(text[ind[i]].t);

		nValues.push_back(norm[ind[i]].x);
		nValues.push_back(norm[ind[i]].y);
		nValues.push_back(norm[ind[i]].z);

		tanValues.push_back(tang[ind[i]].x);
		tanValues.push_back(tang[ind[i]].y);
		tanValues.push_back(tang[ind[i]].z);
	}

	glGenVertexArrays(numVAOs, vao);
	glBindVertexArray(vao[0]);

	glGenBuffers(numVBOs, vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, pValues.size() * sizeof(float), &pValues[0], GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glBufferData(GL_ARRAY_BUFFER, tValues.size() * sizeof(float), &tValues[0], GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
	glBufferData(GL_ARRAY_BUFFER, nValues.size() * sizeof(float), &nValues[0], GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[3]);
	glBufferData(GL_ARRAY_BUFFER, tanValues.size() * sizeof(float), &tanValues[0], GL_STATIC_DRAW);
}

void initallLights(glm::mat4 vMatrix)
{
	glm::vec3 transformed = glm::vec3(vMatrix * glm::vec4(currentLightPos, 1.f));
	lightPos[0] = transformed.x;
	lightPos[1] = transformed.y;
	lightPos[2] = transformed.z;

	// get the locations of the light and material fields in the shader
	globalAmbLoc = glGetUniformLocation(renderingProgram, "globalAmbient");
	ambLoc = glGetUniformLocation(renderingProgram, "light.ambient");
	diffLoc = glGetUniformLocation(renderingProgram, "light.diffuse");
	specLoc = glGetUniformLocation(renderingProgram, "light.specular");
	posLoc = glGetUniformLocation(renderingProgram, "light.position");
	mAmbLoc = glGetUniformLocation(renderingProgram, "material.ambient");
	mDiffLoc = glGetUniformLocation(renderingProgram, "material.diffuse");
	mSpecLoc = glGetUniformLocation(renderingProgram, "material.specular");
	mShinLoc = glGetUniformLocation(renderingProgram, "material.shininess");

	//  set the uniform light and material values in the shader
	glProgramUniform4fv(renderingProgram, globalAmbLoc, 1, globalAmbient);
	glProgramUniform4fv(renderingProgram, ambLoc, 1, lightAmbient);
	glProgramUniform4fv(renderingProgram, diffLoc, 1, lightDiffuse);
	glProgramUniform4fv(renderingProgram, specLoc, 1, lightSpecular);
	glProgramUniform3fv(renderingProgram, posLoc, 1, lightPos);
	glProgramUniform4fv(renderingProgram, mAmbLoc, 1, matAmb);
	glProgramUniform4fv(renderingProgram, mDiffLoc, 1, matDiff);
	glProgramUniform4fv(renderingProgram, mSpecLoc, 1, matSpec);
	glProgramUniform1f(renderingProgram, mShinLoc, matShin);
}

void init(GLFWwindow* window)
{
	renderingProgram = Utils::createShaderProgram("vertShader.vert", "fragShader.frag");

	cameraX = 0.f, cameraY = 0.f, cameraZ = 2.f;
	sphereX = 0.f, sphereY = 0.f, sphereZ = -1.f;
	lightLocX = 3.f, lightLocY = 2.f, lightLocZ = 3.f;

	glfwGetFramebufferSize(window, &width, &height);
	aspect = (float)width / (float)height;
	pMat = glm::perspective(toRadians(45.f), aspect, 0.1f, 1000.f);

	setupVertices();

	moonTexture = Utils::loadTexture("moon.jpg");
	moonNormalMap = Utils::loadTexture("moonNORMAL.jpg");
}

void display(GLFWwindow* window, double currentTime)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0.f, 0.2f, 0.8f, 1.f);

	glUseProgram(renderingProgram);

	deltaTime = currentTime - lastFrame;
	lastFrame = currentTime;

	do_movement();
	//这句必须要有,否则鼠标中键失效
	//pMat = glm::perspective(camera.Zoom, aspect, 0.1f, 1000.f);

	//没有这句,背景就没在相机视点上了,把圆环移到相机的位置
	//mMat = glm::translate(glm::mat4(1.f), glm::vec3(cameraX, cameraY, 4.5f));

	vMat = camera.GetViewMatrix();


	mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
	projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
	nLoc = glGetUniformLocation(renderingProgram, "norm_matrix");

	//vMat = glm::translate(glm::mat4(1.f), glm::vec3(-cameraX, -cameraY, -cameraZ));  //注意相机的Z方向,否则看不到物体
	mMat = glm::translate(glm::mat4(1.f), glm::vec3(sphereX, sphereY, sphereZ));
	mMat = glm::rotate(mMat, toRadians(20.f), glm::vec3(1.f, 0.f, 0.f));
	mMat = glm::rotate(mMat, rotAmt, glm::vec3(0.f, 1.f, 0.f));
	rotAmt += 0.002f;
	mvMat = vMat * mMat;

	invTrMat = glm::transpose(glm::inverse(mvMat));

	currentLightPos = glm::vec3(lightLocX, lightLocY, lightLocZ);
	initallLights(vMat);

	glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
	glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
	glUniformMatrix4fv(nLoc, 1, GL_FALSE, glm::value_ptr(invTrMat));

	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(2);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[3]);
	glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(3);

	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, moonNormalMap);

	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, moonTexture);

	glEnable(GL_CULL_FACE);
	glFrontFace(GL_CCW);

	glDrawArrays(GL_TRIANGLES, 0, numSphereIndices);
}

void window_size_callback(GLFWwindow* window, int newWidth, int newHeight)
{
	//glfwGetFramebufferSize(window, &width, &height);
	glViewport(0, 0, newWidth, newHeight);
	aspect = (float)newWidth / (float)newHeight;
	pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.f);  //1.0472f
}

int main(int argc, char** argv)
{
	int glfwState = glfwInit();
	if (glfwState == GLFW_FALSE)
	{
		cout << "Initialize GLFW failed,invoke glfwInit()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;
		glfwTerminate();
		exit(EXIT_FAILURE);
	}

	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
	glfwWindowHint(GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_PROFILE);
	glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);

	GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "Texture plus normal", nullptr, nullptr);
	if (!window)
	{
		cout << "GLFW create window failed,invoke glfwCreateWindow()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;
		glfwTerminate();
		exit(EXIT_FAILURE);
	}

	glfwMakeContextCurrent(window);

	//glfwSetWindowSizeCallback(window, window_size_callback);

	int glewState = glewInit();
	if (GLEW_OK != glewState)
	{
		cout << "GLEW initialize failed, invoke glewInit()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;
		glfwTerminate();
		exit(EXIT_FAILURE);
	}

	glfwSwapInterval(1);

	glfwSetWindowSizeCallback(window, window_size_callback);
	glfwSetCursorPosCallback(window, mouse_move_callback);
	glfwSetScrollCallback(window, mouse_scroll_callback);
	glfwSetKeyCallback(window, key_press_callback);

	init(window);

	while (!glfwWindowShouldClose(window))
	{
		display(window, glfwGetTime());
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glfwDestroyWindow(window);
	glfwTerminate();
	exit(EXIT_SUCCESS);

	return 0;
}

2.着色器程序

1.顶点着色器

#version 460 core

layout(location = 0) in vec3 vertPos;
layout(location = 1) in vec2 texCoords;
layout(location = 2) in vec3 vertNormal;
layout(location = 3) in vec3 vertTangent;

out vec3 varyingLightDir;
//out vec3 varyingLightPos;
out vec3 varyingVertPos;
out vec3 varyingNormal;
out vec3 varyingTangent;
out vec3 originalVertex;
out vec2 tc;

layout(binding = 0) uniform sampler2D s;
layout(binding = 1) uniform sampler2D t;

struct PositionalLight
{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;
	vec3 position;
};

struct Material
{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;
	float shininess;
};

uniform vec4 globalAmbient;
uniform PositionalLight light;
uniform Material material;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 norm_matrix;

void main(void)
{
	varyingVertPos = (mv_matrix * vec4(vertPos, 1.f)).xyz;
	varyingLightDir = light.position - varyingVertPos;
	tc = texCoords;

	originalVertex = vertPos;

	varyingNormal = (norm_matrix * vec4(vertNormal, 1.f)).xyz;
	varyingTangent = (norm_matrix * vec4(vertTangent, 1.f)).xyz;

	gl_Position = proj_matrix * mv_matrix * vec4(vertPos, 1.f);
}

2.片元着色器

#version 460 core

in vec3 varyingLightDir;
in vec3 varyingVertPos;
in vec3 varyingNormal;
in vec3 varyingTangent;
in vec3 originalVertex;
in vec2 tc;

out vec4 fragColor;

layout(binding = 0) uniform sampler2D s;
layout(binding = 1) uniform sampler2D t;

struct PositionalLight
{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;
	vec3 position;
};

struct Material
{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;
	float shininess;
};

uniform vec4 globalAmbient;
uniform PositionalLight light;
uniform Material material;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 norm_matrix;

vec3 calcNewNormal()
{
	vec3 normal = normalize(varyingNormal);
	vec3 tangent = normalize(varyingTangent);
	tangent = normalize(tangent - dot(tangent, normal) * normal);
	vec3 bitangent = cross(tangent, normal);
	mat3 tbn = mat3(tangent, bitangent, normal);
	vec3 retrivedNormal = texture(s, tc).xyz;
	retrivedNormal = retrivedNormal * 2.f - 1.f;
	vec3 newNormal = tbn * retrivedNormal;
	newNormal = normalize(newNormal);
	
	return newNormal;
}

void main(void)
{
	// normalize the light, normal, and view vectors:
	vec3 L = normalize(varyingLightDir);
	vec3 V = normalize(-varyingVertPos);
	vec3 N = calcNewNormal();

	// get the angle between the light and surface normal:
	float cosTheta = dot(L, N);

	// compute light reflection vector, with respect N:
	vec3 R = normalize(reflect(-L, N));   //以表面顶点为基准,入射光和反射光方向一致

	// angle between the view vector and reflected light:
	float cosPhi = dot(V, R);

	vec4 texC = texture(t, tc);

	// compute ADS contributions with surface texture image:
	fragColor = globalAmbient + light.ambient * texC																
				+ light.diffuse * texC * max(cosTheta, 0.f)
				+ light.specular *texC * pow(max(cosPhi, 0.f), material.shininess);
	
}

运行效果

openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节_第8张图片

源码下载

源码下载地址

你可能感兴趣的:(openGL,openGL纹理贴图,openGL法线贴图,openGL纹理法线贴图结合)