openGL增强表面细节--高度贴图

openGL系列文章目录

前言

现在我们扩展法线贴图的概念——从纹理图像用于扰动法向量到扰乱顶点位置本身。实
际上,以这种方式修改对象的几何体具有一定的优势,例如使表面特征沿着对象的边缘可
见,并使特征能够响应阴影贴图。我们将会看到,它还可以帮助构建地形。
一种实用的方法是使用纹理图像来存储高度值,然后使用该高度值来提升(或降低)顶
点位置。含有高度信息的图像称为高度图,使用高度图更改对象的顶点的方法称为高度贴
图①。高度图通常将高度信息编码为灰度颜色:(0,0,0)(黑色)=低高度,(1,1,1)(白色)=
高高度。这样一来通过算法或使用“画图”程序就可以轻松创建高度图。图像的对比度越
高,其表示的高度变化越大。这些概念将在图1(显示随机生成的地图)和图1(显
示有组织的模式的地图)中说明。
在这里插入图片描述
图1
改变顶点位置是否有用取决于改变的模型。顶点操作可以在顶点着色器中轻松完成,当
模型顶点细节级别够高(例如在足够高精度的球体中)时,改变顶点高度的方法效果很好。
但是,当模型的顶点数量很少(例如立方体的角)时,渲染对象的表面需要依赖于光栅器
中的顶点插值来填充细节。当顶点着色器中可用于改变高度的顶点很少时,许多像素的高
度将无法从高度图中检索,而需要由插值生成,从而导致表面细节较差。当然,在片段着
色器中是不可能进行顶点操作的,因为这时顶点已被光栅化为像素位置。
程序10.4 展示了一个将顶点“向外”(即在表面法向量的方向上)移动的顶点着色器代
码。它通过将顶点法向量乘以从高度图检索所得的值,然后将该乘积与顶点位置相加,以
“向外”移动顶点。
在这里插入图片描述
图2

一、高度贴图原理

图10.14(见彩插)展示了通过在画图程序中涂鸦创建的简单高度图(左上角)。高度图
图像中还绘制了一个白色矩形。绿色版本的高度图(左下角)用作纹理。使用程序10.4 中
展示的着色器将高度图应用于100×100 的矩形网格模型时,会产生类似“地形”的感觉(如
图10.14 右图所示)。注意白色矩形是如何生成右边的悬崖的。
图10.14 展示的渲染结果还算可以,因为模型(网格和球体)有足够数量的顶点来对高
度贴图值进行采样。也就是说,模型具有大量的顶点,而高度图相对粗糙并且以低分辨率
充分地采样。然而,仔细观察仍然会发现存在分辨率伪影,例如沿图10.14 中地形右侧凸起
的矩形盒子的左下边缘。凸起的矩形盒子两侧看起来不是完美矩形,而且颜色有渐变效果,
其原因是底层网格100 像素×100 像素的分辨率无法与高度图中的白色矩形完全对齐,从而
导致纹理的光栅化坐标沿侧面产生伪影。
当尝试将其应用于要求更严苛的高度贴图时,在顶点着色器中进行高度贴图的限制会进
一步暴露。考虑图10.5 中展示的月球图像。法线贴图在捕获图像细节方面表现非常出色(如
图10.9 和图10.11 所示),而且由于它是灰度图,因此尝试将其作为高度图应用似乎很自然。
但是,基于顶点着色器的高度贴图会无法胜任这个任务,因为顶点着色器中采样的顶点数
(即使对于精度=500 的球体)比起图像中的细节级别,仍然太少。相较之下,法线贴图能够
很好地捕获细节,因为在片段着色器中对法线贴图的采样是像素级的。
在这里插入图片描述
图3

二、代码实现

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 "ImportedModel.h"
#include "Utils.h"

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;
int width = 0, height = 0;

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

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

float cameraX = 0.f, cameraY = 0.f, cameraZ = 0.f;
float gndLocX = 0.f, gndLocY = 0.f, gndLocZ = 0.f;
GLuint mvLoc = 0, projLoc = 0;
float aspect = 0.f;
glm::mat4 mMat(1.f), vMat(1.f), pMat(1.f), mvMat(1.f);

ImportedModel ground("grid.obj");
int numGroundVertices = 0;

GLuint heightMap = 0;
GLuint heightTexture = 0;


Camera camera(glm::vec3(0.f, 0.f, 1.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)
{
	numGroundVertices = ground.getNumVertices();
	vector<glm::vec3> vert = ground.getVertices();
	vector<glm::vec2> text = ground.getTextureCoords();
	vector<glm::vec3> norm = ground.getNormals();

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

	for (int i=0; i<ground.getNumVertices(); 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);
}

void init(GLFWwindow* window)
{
	renderingProgram = Utils::createShaderProgram("vertShader.vert", "fragShader.frag");
	cameraX = 0.03f, cameraY = 0.03, cameraZ = 0.8f;
	gndLocX = 0.f, gndLocY = 0.f, gndLocZ = 0.f;

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

	setupVertices();

	heightTexture = Utils::loadTexture("heightTexture.jpg");
	heightMap = Utils::loadTexture("height.jpg");
}

void display(GLFWwindow* window, double currentTime)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0.0f, 0.2f, 0.5f, 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");

	//vMat = glm::translate(glm::mat4(1.f), glm::vec3(-cameraX, -cameraY, -cameraZ));
	mMat = glm::translate(glm::mat4(1.f), glm::vec3(gndLocX, gndLocY, gndLocZ));
	mMat = glm::rotate(mMat, toRadians(25.f), glm::vec3(1.f, 0.f, 0.f));

	mvMat = vMat * mMat;

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

	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);

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

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

	glEnable(GL_CULL_FACE);
	glFrontFace(GL_CCW);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	glDrawArrays(GL_TRIANGLES, 0, ground.getNumVertices());
}

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

int main(int argc, char** argv)
{
	int glfwState = glfwInit();
	if (glfwState == GLFW_FALSE)
	{
		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, GLFW_TRUE);

	GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "Height Mapped Terrain", 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 460 core

layout(location = 0) in vec3 vertPos;
layout(location = 1) in vec2 texCoord;
layout(location = 2) in vec3 vertNormal;

out vec2 tc;

uniform mat4 mv_matrix;
uniform mat4 proj_matrix;

layout(binding = 0) uniform sampler2D t; // for texture
layout(binding = 1) uniform sampler2D h; // for height map

void main(void)
{
	vec4 p = vec4(vertPos, 1.f) + vec4(vertNormal * ((texture(h, texCoord).r / 5.f)), 1.f);
	tc = texCoord;
	// "p"是高度图所改变的顶点位置
	// 由于高度图是灰度图,因此使用其任何颜色分量
	// 都可以(我们使用"r")。除以5.0 用来调整高度
	gl_Position = proj_matrix * mv_matrix * p;
}

2.片元着色器

#version 460 core

in vec2 tc;
out vec4 fragColor;

uniform mat4 mv_matrix;
uniform mat4 proj_matrix;

layout(binding = 0) uniform sampler2D t; // for texture
layout(binding = 1) uniform sampler2D h; // for height map

void main(void)
{
	fragColor = texture(t, tc);
}

运行效果

在这里插入图片描述

源码下载

源码下载地址

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
opengles是一种用于在移动设备和嵌入式系统上实现2D和3D图形渲染的图形库。而gl-transitions是一个开源的OpenGL库,用于创建平滑过渡效果,可以应用于图像、视频等多种媒体内容。 要将gl-transitions移植到opengles上,需要进行以下步骤: 1. 确定opengles版本:gl-transitions可能使用的是OpenGL的较新版本,而opengles可能只支持较旧的版本。因此,首先需要确定opengles版本,并了解其与OpenGL之间的差异。 2. 了解gl-transitions的实现:深入了解gl-transitions的实现方式和代码结构,理解其对OpenGL的使用方式和功能。 3. 理解opengles的API:熟悉opengles的API,包括顶点/片段着色器、缓冲区对象、纹理对象等。理解opengles的渲染管线和数据传递方式,以便能够正确地将gl-transitions移植到opengles上。 4. 逐步移植:根据gl-transitions的实现和opengles的API,逐步将gl-transitions的代码移植为opengles可用的代码。这可能涉及到对着色器代码的修改、纹理对象的创建和绑定、缓冲区对象的使用等。 5. 调试和测试:移植完成后,进行调试和测试以确保移植后的代码在opengles上正常工作,并且能够正确地渲染出所需的过渡效果。 需要注意的是,由于较新版本的OpenGL可能具有一些opengles不支持的功能,因此在移植过程中可能需要做一些功能的调整或替代。此外,移植过程中可能还需要考虑设备的性能和兼容性问题,确保移植后的代码能够在目标设备上流畅地运行。 总结起来,将gl-transitions移植到opengles上需要对opengles的API有很好的了解,并根据它的渲染方式和数据传递方式对gl-transitions的代码进行适当的修改和调整。这样才能确保移植后的代码能够在opengles上正常运行并呈现出所需的过渡效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值