openGL增强表面细节--凹凸贴图具体实现

本文介绍了在OpenGL中如何通过凹凸贴图和法线贴图来模拟不规则表面的光照效果,从而在平滑模型上实现逼真的纹理渲染。作者通过实例展示了如何在片段着色器中使用过程式凹凸贴图技术,调整像素处的法向量以产生高尔夫球般的环面效果。此外,代码部分提供了C++和GLSL着色器的实现,演示了如何创建和设置顶点、法向量,并进行光照计算,以达到动态光照效果。
摘要由CSDN通过智能技术生成

openGL系列文章目录

前言

假设我们想要对不规则表面的物体进行建模,例如橘子凹凸的表皮、葡萄干褶皱的表面
或月球的陨石坑表面。我们该怎么做?到目前为止,我们已经学会了两种可能的方法:(a)
我们可以对整个不规则表面进行建模,但这么做通常不切实际(一个有许多坑的表面需要
大量的顶点);(b)我们可以将不规则表面的纹理图图像应用于平滑的对象。第二种选择通
常比较高效。但是,如果场景中有光源,当光源(或摄像机角度)移动时,我们很快就会
发现物体使用了静态纹理渲染(以及物体表面是平滑的),因为纹理上的亮区和暗区不会像
真正凹凸不平的表面那样,随着光源或摄像机移动而改变。
在本章中,我们将探讨几种与实现凹凸表面相关的方法,通过使用光照效果,即使在实
际对象模型表面平滑的情况下,也能使对象看起来具有逼真的表面纹理。我们将首先观察
凹凸贴图和法线贴图,当直接为对象添加微小表面细节会使得计算代价过高时,它们可以
为场景中的对象增加相当程度的真实感。我们还将研究通过高度贴图实际扰乱光滑表面中
顶点的方法,这对于生成地形(和其他一些用途)非常有用。

一、凹凸贴图

,我们了解了表面法向量在创建令人信服的光照效果中是至关重要的。像素处
的光强度主要由反射角确定,即需要考虑到光源位置、相机位置和像素处的法向量。因此,如
果我们能找到生成相应法向量的方法,就可以避免生成与凹凸不平或褶皱表面相对应的顶点。
图1 展示了对于单个“凸起”修改法向量的概念。图1 用于凹凸贴图的扰动法向量异
在这里插入图片描述
图1
因此,如果我们想让一个物体看起来好像有凹凸(或皱纹,陨石坑等),一种方法是计
算当表面确实凹凸不平时其上的法向量。当场景点亮时,光照会让人产生我们所期望的幻
觉。这是Blinn 在1978 年首次提出的[BL78],随着在片段着色器拥有了可以对每个像素进行
光照计算的能力,这种方法就变得切实可行了。
程序10.1 中展示了顶点着色器和片段着色器的一个示例,这段程序会生成一个带有“高
尔夫球”表面的环面,如图2 所示。其代码几乎与我们之前在程序中看到的相同。片
段着色器中唯一显著的变化是——输入的已插值法向量(在原程序中名为“varyingNormal”)
在这里变得凹凸不平了,其方法是对环面模型的原始(未变形)顶点的X、Y 和Z 轴应用正弦
函数。请注意,这里需要顶点着色器将未经变换的顶点沿管线传递给片段着色器。以这种方式对法向量进行改变,即在运行时使用数学函数进行计算,称为过程式凹凸贴图。
在这里插入图片描述
图2

二、代码

1.主程序c++

#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 "Torus.h"
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

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

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

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

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

float torusLocX = 0.f, torusLocY = 0.f, torusLocZ = 0.f;
float cameraLocX = 0.f, cameraLocY = 0.f, cameraLocZ = 8.f;
float lightLocX = 0.f, lightLocY = 0.f, lightLocZ = 0.f;


// variable allocation for display
GLuint mvLoc = 0, projLoc = 0, nLoc = 0;
int width = 0, height = 0;
float aspect = 0.f;
glm::mat4 mMat(1.f), vMat(1.f), pMat(1.f), mvMat(1.f), invTrMat(1.f);
GLuint globalAmbLoc = 0, ambLoc = 0, diffLoc = 0, specLoc = 0, posLoc = 0, mAmbLoc = 0, mDiffLoc = 0, mSpecLoc = 0, mShinLoc;
glm::vec3 currentLightPos(0.f);
float lightPos[3];
Torus myTorus(0.8f, 0.2f, 48);
int numTorusVertices = 0, numTorusIndices = 0;
Camera myCamera(glm::vec3(0.f, 0.f, 4.f));

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

// gold material
float* matAmb = Utils::goldAmbient();
float* matDiff = Utils::goldDiffuse();
float* matSpec = Utils::goldSpecular();
float matShin = Utils::goldShininess();

Camera camera(glm::vec3(0.f, 0.f, 5.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)
{
	numTorusVertices = myTorus.getNumVertices();
	numTorusIndices = myTorus.getNumIndices();

	vector<int> ind = myTorus.getIndices();
	vector<glm::vec3> vert = myTorus.getVertices();
	vector<glm::vec2> text = myTorus.getTexCoords();
	vector<glm::vec3> norm = myTorus.getNormals();

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

	for (int i=0; i<numTorusVertices; i++)
	{
		pValues.push_back(vert[i].x);
		pValues.push_back(vert[i].y);
		pValues.push_back(vert[i].z);

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

		nValues.push_back(norm[i].x);
		nValues.push_back(norm[i].y);
		nValues.push_back(norm[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_ELEMENT_ARRAY_BUFFER, vbo[3]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size() * sizeof(int), &ind[0], GL_STATIC_DRAW);
}

//把光变换到相机空间
void installLights(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
	//glUniformMatrix4fv(globalAmbLoc, 1, GL_FALSE, glm::value_ptr());
	glProgramUniform4fv(renderingProgram, globalAmbLoc, 1, globalAmbient);
	glProgramUniform4fv(renderingProgram, ambLoc, 1, lightAmbient);
	glProgramUniform4fv(renderingProgram, diffLoc, 1, lightDiffuse);
	glProgramUniform4fv(renderingProgram, specLoc, 1, lightSPecular);
	glProgramUniform4fv(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");
	cameraLocX = 0.f, cameraLocY = 0.f, cameraLocZ = 2.f;
	torusLocX = 0.f, torusLocY = 0.f, torusLocZ = -1.f;

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

	lightLocX = 5.f, lightLocY = 2.f, lightLocZ = 2.f;
	setupVertices();
}

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

	glUseProgram(renderingProgram);

	deltaTime = currentTime - lastFrame;
	lastFrame = currentTime;

	do_movement();
	//这句必须要有,否则鼠标中键失效
	pMat = glm::perspective(camera.Zoom, aspect, 0.01f, 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(-cameraLocX, -cameraLocY, -cameraLocZ));
	mMat = glm::translate(glm::mat4(1.f), glm::vec3(torusLocX, torusLocY, torusLocZ));
	mMat = glm::rotate(mMat, toRadians(45.f), glm::vec3(1.f, 0.f, 0.f));
	mvMat = vMat * mMat;
	invTrMat = glm::transpose(glm::inverse(mvMat));

	currentLightPos = glm::vec3(lightLocX, lightLocY, lightLocZ);
	installLights(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[2]);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);

	glEnable(GL_CULL_FACE);
	glFrontFace(GL_CCW);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
	glDrawElements(GL_TRIANGLES, myTorus.getIndices().size(), GL_UNSIGNED_INT, 0);
}

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

int main(int argc, char** argv)
{
	int glfwState = glfwInit();
	if (GLFW_FALSE == glfwState)
	{
		cout << "GLFW initialize 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, "Proc bump", nullptr, nullptr);
	if (!window)
	{
		cout << "GLFW create window failed,invoke glfwCreateWindow()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;
		glfwTerminate();
		exit(EXIT_FAILURE);
	}

	glfwMakeContextCurrent(window);

	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 430

layout (location = 0) in vec3 vertPos;
layout (location = 1) in vec3 vertNormal;
out vec3 varyingNormal;
out vec3 varyingLightDir;
out vec3 varyingVertPos;

out vec3 originalVertex;

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.0)).xyz;
	varyingLightDir = light.position - varyingVertPos;
	varyingNormal = (norm_matrix * vec4(vertNormal,1.0)).xyz;
	
	originalVertex = vertPos;

	gl_Position = proj_matrix * mv_matrix * vec4(vertPos,1.0);
}

2.片元着色器

#version 430

in vec3 varyingNormal;
in vec3 varyingLightDir;
in vec3 varyingVertPos;

in vec3 originalVertex;

out vec4 fragColor;

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)
{	// normalize the light, normal, and view vectors:
	vec3 L = normalize(varyingLightDir);
	vec3 N = normalize(varyingNormal);
	vec3 V = normalize(-varyingVertPos);
	
	float a = 0.25;		// controls depth of bumps
	float b = 100.0;	// controls width of bumps
	float x = originalVertex.x;
	float y = originalVertex.y;
	float z = originalVertex.z;
	N.x = varyingNormal.x + a*sin(b*x);
	N.y = varyingNormal.y + a*sin(b*y);
	N.z = varyingNormal.z + a*sin(b*z);
	N = normalize(N);
	
	// compute light reflection vector, with respect N:
	vec3 R = normalize(reflect(-L, N));
	
	// get the angle between the light and surface normal:
	float cosTheta = dot(L,N);
	
	// angle between the view vector and reflected light:
	float cosPhi = dot(V,R);

	// compute ADS contributions (per pixel):
	fragColor = globalAmbient * material.ambient
	+ light.ambient * material.ambient
	+ light.diffuse * material.diffuse * max(cosTheta,0.0)
	+ light.specular  * material.specular *
		pow(max(cosPhi,0.0), material.shininess);
}

在这里插入图片描述

运行效果

在这里插入图片描述
注意,在片元着色器中:

vec3 R = normalize(reflect(-L, N));

写成:

vec3 R = normalize(reflect(L, N));

运行效果是这样的:

看不见凹凸表面高光效果
在这里插入图片描述

源码下载

源码下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值