【BumpMap】凹凸贴图绘制简单图形

BumpMap 的主要思想就是通过 (x + dx, y), (x - dx, y), (x, y + dy), (x, y - dy) 这四个点的高度计算出(x, y) 点法向量的改变量:

if (i != XMAX)
	normal[i][j] = normal[i][j] - (position[i][j] - position[i + 1][j]).Unitization() * (height[i][j] - height[i + 1][j]);
if (i != 0)
	normal[i][j] = normal[i][j] - (position[i][j] - position[i - 1][j]).Unitization() * (height[i][j] - height[i - 1][j]);

if (j != YMAX)
	normal[i][j] = normal[i][j] - (position[i][j] - position[i][j + 1]).Unitization() * (height[i][j] - height[i][j + 1]);
if (j != 0)
	normal[i][j] = normal[i][j] - (position[i][j] - position[i][j - 1]).Unitization() * (height[i][j] - height[i][j - 1]);

在实现过程中我们取 dx, dx 为相邻的格点就可以方便计算啦!其中 Unitization() 是单位化操作。


尝试理解一下第二行的运算,如果 (i + 1, j) 的高度要低于 (i, j),显然 (i, j) 点的法向量应该偏向 (i + 1, j) 的方向。

(position[i][j] - position[i + 1][j]).Unitization()

是从 (i + 1, j) 指向 (i, j) 的单位向量,如果
(height[i][j] - height[i + 1][j])
是一个正值,那么
- (position[i][j] - position[i + 1][j]).Unitization() * (height[i][j] - height[i + 1][j])
就会沿着 (i, j) 到 (i + 1, j) 的方向,也就是normal应有的改变量。


还有一个小难点是建立球体的模型,我选择了使用三角片去贴满球体的表面,这里涉及到了球坐标到直角坐标的变换。

float theta = i * 2.0 * PI / XMAX, fai = j * PI / YMAX;

position[i][j].x = 1.0 * sin(fai) * sin(theta);
position[i][j].y = 1.0 * cos(fai);
position[i][j].z = 1.0 * sin(fai) * cos(theta);
而直角坐标的优点就是,(i, j) 的法线和 (i, j) 的坐标是一致的。

	normal[i][j] = position[i][j];

剩下的就是用OpenGL绘制三角形和设置光照属性啦!就很简单~


附完整代码【当然可能有错咯】:

#include <GL/freeglut.h>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include "MyMath.h"

using namespace std;

const int XINIT = 800, YINIT = 800;

const int XMAX = 1600, YMAX = 800;

float height[XMAX + 1][YMAX + 1];

int XCURR = XINIT, YCURR = YINIT;

const int MY_POLYGON = 0, MY_SPHERE = 1;

const int MY_MODE = MY_SPHERE;

Vector3f position[XMAX + 1][YMAX + 1], normal[XMAX + 1][YMAX + 1];

void InitNormal() {
	for (int i = 0; i <= XMAX; i++)
		for (int j = 0; j <= YMAX; j++) {
			if (MY_MODE == MY_POLYGON) {
				normal[i][j].x = 0.0;
				normal[i][j].y = 0.0;
				normal[i][j].z = 1.0;
			}

			if (MY_MODE == MY_SPHERE) {
				normal[i][j] = position[i][j];
			}

			if (i != XMAX)
				normal[i][j] = normal[i][j] - (position[i][j] - position[i + 1][j]).Unitization() * (height[i][j] - height[i + 1][j]);
			if (i != 0)
				normal[i][j] = normal[i][j] - (position[i][j] - position[i - 1][j]).Unitization() * (height[i][j] - height[i - 1][j]);

			if (j != YMAX)
				normal[i][j] = normal[i][j] - (position[i][j] - position[i][j + 1]).Unitization() * (height[i][j] - height[i][j + 1]);
			if (j != 0)
				normal[i][j] = normal[i][j] - (position[i][j] - position[i][j - 1]).Unitization() * (height[i][j] - height[i][j - 1]);

			normal[i][j].Unitization();
		}
}

void RenderScene() {
	glClearColor(0.0, 0.0, 0.0, 1.0);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPointSize(10.0f);
	glLineWidth(1.0f);

	glPushMatrix();

	{
		GLfloat sunLightPosition[] = {1.0f, 1.0f, 1.0f, 0.0f};
		GLfloat sunLightAmbient[] = {0.6f, 0.6f, 0.6f, 1.0f};
		GLfloat sunLightDiffuse[] = {0.6f, 0.6f, 0.6f, 1.0f};
		GLfloat sunLightSpecular[] = {1.0f, 1.0f, 1.0f, 1.0f};

		glLightfv(GL_LIGHT0, GL_POSITION, sunLightPosition);
		glLightfv(GL_LIGHT0, GL_AMBIENT, sunLightAmbient);
								   
		glLightfv(GL_LIGHT0, GL_DIFFUSE, sunLightDiffuse);
		glLightfv(GL_LIGHT0, GL_SPECULAR, sunLightSpecular);

		glEnable(GL_LIGHT0);
		glEnable(GL_LIGHTING);
	}
/*	
	static float rotateTheta = 0.0;

	glRotated(-23.5, 0.0, 0.0, 1.0);

	glRotated(rotateTheta, 0.0, 1.0, 0.0);

	rotateTheta += 1;
*/

	{
		GLfloat triangleMatAmbient[] = {0.6f, 0.6f, 0.6f, 1.0f};
		GLfloat triangleMatDiffuse[] = {0.6f, 0.6f, 0.6f, 1.0f};
		GLfloat triangleMatSpecular[] = {1.0f, 1.0f, 1.0f, 1.0f};
		GLfloat triangleMatEmission[] = {0.1f, 0.1f, 0.3f, 1.0f};
		GLfloat triangleMatShininess = 100.0f;

		glMaterialfv(GL_FRONT, GL_AMBIENT, triangleMatAmbient);
		glMaterialfv(GL_FRONT, GL_DIFFUSE, triangleMatDiffuse);
		glMaterialfv(GL_FRONT, GL_SPECULAR, triangleMatSpecular);
		glMaterialfv(GL_FRONT, GL_EMISSION, triangleMatEmission);
		glMaterialf(GL_FRONT, GL_SHININESS, triangleMatShininess);
	}

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	glBegin(GL_TRIANGLES);

	{
		for (int i = 0; i < XMAX; i++)
			for (int j = 0; j < YMAX; j++) {
				glNormal3fv(normal[i][j]());
				glVertex3fv(position[i][j]());

				glNormal3fv(normal[i + 1][j]());
				glVertex3fv(position[i + 1][j]());

				glNormal3fv(normal[i][j + 1]());
				glVertex3fv(position[i][j + 1]());
			}

		for (int i = 0; i < XMAX; i++)
			for (int j = 0; j < YMAX; j++) {
				glNormal3fv(normal[i + 1][j]());
				glVertex3fv(position[i + 1][j]());

				glNormal3fv(normal[i + 1][j + 1]());
				glVertex3fv(position[i + 1][j + 1]());

				glNormal3fv(normal[i + 1][j + 1]());
				glVertex3fv(position[i][j + 1]());
			}
	}

	glEnd();

	glPopMatrix();

	glutSwapBuffers();
}

void ReshapeWindow(int w, int h) {
	glViewport(0, 0, w, h);

	XCURR = w;
	YCURR = h;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(2 * atan(h / (YINIT * 5.0)) / PI * 180, 1.0 * w / h, 0.5, 18);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0f, 1.0f, 0.0);
}

void MouseMotion(int x, int y) {
	float xFloat = (x - XCURR * 0.5f) / (YINIT * 0.5);
	float yFloat = (YCURR * 0.5f - y) / (YINIT * 0.5);

	if (xFloat < -1.0 || xFloat > 1.0 || yFloat < -1.0 || yFloat > 1.0)
		return;

	int xInt = floor((xFloat + 1.0) * 0.5 * XMAX);
	int yInt = floor((yFloat + 1.0) * 0.5 * YMAX);

	height[xInt + 0][yInt + 0] = -3;
	height[xInt + 1][yInt + 0] = -3;
	height[xInt + 0][yInt + 1] = -3;
	height[xInt + 1][yInt + 1] = -3;
}

void MyInit() {
	for (int i = 0; i <= XMAX; i++)
		for (int j = 0; j <= YMAX; j++) {
			{
				if (i % 80 >= 20 && i % 80 < 60 && j % 80 >= 20 && j % 80 < 60)
					height[i][j] = 2.0;
				else
					height[i][j] = 0.0;
			}

			if (MY_MODE == MY_POLYGON) {
				position[i][j].x = i * 2.0 / XMAX - 1.0;
				position[i][j].y = j * 2.0 / YMAX - 1.0;
				position[i][j].z = 0.0;
			}


			if (MY_MODE == MY_SPHERE) {
				float theta = i * 2.0 * PI / XMAX, fai = j * PI / YMAX;

				position[i][j].x = 1.0 * sin(fai) * sin(theta);
				position[i][j].y = 1.0 * cos(fai);
				position[i][j].z = 1.0 * sin(fai) * cos(theta);
			}
		}
	
	InitNormal();
}

int main(int argc, char** argv) {
	glutInit(&argc, argv);

	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);

	glutInitWindowPosition(800, 40);

	glutInitWindowSize(XINIT, YINIT);

	glutCreateWindow("OpenGL");

	glEnable(GL_POINT_SMOOTH);

	glEnable(GL_LINE_SMOOTH);

	glEnable(GL_DEPTH_TEST);

	glutDisplayFunc(RenderScene);

	glutReshapeFunc(ReshapeWindow);

//	glutIdleFunc(RenderScene);

//	glutMotionFunc(MouseMotion);

	MyInit();

	glutMainLoop();
}

这个程序还有一些别的功能哦!


以下是用到的数据结构【其实只有一个】:

#pragma once
#include <math.h>
#include <iostream>

using namespace std;

const float PI = 3.1415926f;

class Vector3f {
public:
	float x, y, z, *array;

	Vector3f(float x = 0.0, float y = 0.0, float z = 0.0)
		: x(x), y(y), z(z) {
		array = new float[3];
	}

	Vector3f operator * (const float lambda) {
		return Vector3f(x * lambda, y * lambda, z * lambda);
	}

	Vector3f operator + (const Vector3f & tempVector3f) {
		return Vector3f(x + tempVector3f.x, y + tempVector3f.y,
			z + tempVector3f.z);
	}
	
	Vector3f operator - (const Vector3f & tempVector3f) {
		return Vector3f(x - tempVector3f.x, y - tempVector3f.y,
			z - tempVector3f.z);
	}

	Vector3f operator * (const Vector3f & tempVector3f) {
		return Vector3f(y * tempVector3f.z - z * tempVector3f.y,
			z * tempVector3f.x - x * tempVector3f.z,
			x * tempVector3f.y - y * tempVector3f.x);
	}

	Vector3f & operator = (const Vector3f & tempVector3f) {
		x = tempVector3f.x;
		y = tempVector3f.y;
		z = tempVector3f.z;

		return (*this);
	}

	~Vector3f() {
		delete[] array;
	}

	float Modulus() {
		return sqrt(x * x + y * y + z * z);
	}

	Vector3f & Unitization() {
		float modulus = Modulus();
		x /= modulus;
		y /= modulus;
		z /= modulus;

		return (*this);
	}

	float * operator () () {
		array[0] = x;
		array[1] = y;
		array[2] = z;
		return array;
	}

	friend ostream & operator << (ostream & out, const Vector3f & vec) {
		return out << vec.x << " " << vec.y << " " << vec.z;
	}
};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值