海面模拟以及渲染(计算着色器、FFT、Reflection Matrix)

一、海面网格的波形计算


大体思路: 
如上图公式所示,该公式等号左边的向量X表示的是顶点位置,t表示时间,等式右边的向量k可以理解为表示的是单位根向量,即可以认为等式右边是等式左边的离散傅里叶变换(详见: 一小时学会快速傅里叶变换)后的形式,根据理论,我们可以很轻松的算出h(k,t)的数值,所以我们需要使用傅里叶逆变换来得到h(x,t)的数值,该数值即为每个顶点的高度值。得到高度值后,我们接下来要计算每个顶点的水平扰动值(用来模拟出海面波尖的效果,如果不计算的话则是模拟出比较平静的海面状况)以及法线向量。

为了提高性能,可以将波形计算交给GPU来做,使用计算着色器是一个不错的选择。
关于波形计算的代码:
GLfloat  RandomUniform(const GLfloat start, const GLfloat end)
{
	return ((GLfloat)rand() / (GLfloat)RAND_MAX) * (end - start) + start;
}

GLfloat  RandomNormal(const GLfloat mean, const GLfloat standardDeviation)
{
	GLfloat x1, x2;

	x1 = RandomUniform(GLUS_UNIFORM_RANDOM_BIAS, 1.0f - GLUS_UNIFORM_RANDOM_BIAS);
	x2 = RandomUniform(0.0f, 1.0f);

	return mean + standardDeviation * (sqrtf(-2.0f * logf(x1)) * cosf(2.0f * GL_PI * x2));
}

GLfloat OceanMeshForGPU::PhillipsSpectrum(GLfloat A, GLfloat L, glm::vec2 waveDirection, glm::vec2 windDirection)
{
	GLfloat k = glm::length(waveDirection);
	GLfloat waveDotWind = glm::dot(waveDirection, windDirection);

	if (L == 0.0f || k == 0.0f)
	{
		return 0.0f;
	}

	return A * expf(-1.0f / (k * L * k * L)) / (k * k * k * k) * waveDotWind * waveDotWind;
}

GLboolean OceanMeshForGPU::Init()
{
	glm::vec3 lightDirection = { 0.5f, 1.0f, 1.0f };
	glm::vec4 color = { 0.0f, 0.8f, 0.8f, 1.0f };

	GLUSshape gridPlane;

	GLint i, k;
	glm::mat4 matrix = glm::mat4();

	GLfloat* h0Data;

	GLint* butterflyIndices;
	GLfloat* butterflyIndicesAsFloat;

	glm::vec2 waveDirection;
	glm::vec2 windDirection = WIND_DIRECTION;
	GLfloat phillipsSpectrumValue;

	//通过项数计算出计算 FFT 的时候需要的迭代总次数
	GLint steps = 0;
	GLint temp = N;
	while (!(temp & 0x1))
	{
		temp = temp >> 1;
		steps++;
	}
	//声明更新海面网格的计算着色器
	g_computeUpdateHtProgram = new Shader("OceanUpdate.comp");
	//声明进行FFT的计算着色器
	g_computeFftProgram = new Shader("OceanFFT.comp");
	//声明更新法线向量的计算着色器
	g_computeUpdateNormalProgram = new Shader("OceanUpdateNormal.comp");
	//声明进行渲染的顶点和像素着色器
	g_program = new Shader("OceanGPU.vs", "OceanGPU.frag");
	
	g_totalTimeUpdateHtLocation = glGetUniformLocation(g_computeUpdateHtProgram->Program , "u_totalTime");

	g_processColumnFftLocation = glGetUniformLocation(g_computeFftProgram->Program, "u_processColumn");
	g_stepsFftLocation = glGetUniformLocation(g_computeFftProgram->Program, "u_steps");

	g_ModelLoc = glGetUniformLocation(g_program->Program, "u_Model");
	g_ViewLoc = glGetUniformLocation(g_program->Program, "u_View");
	g_ProjectionLoc = glGetUniformLocation(g_program->Program, "u_Projection");
	g_normalMatrixLocation = glGetUniformLocation(g_program->Program, "u_normalMatrix");
	g_lightDirectionLocation = glGetUniformLocation(g_program->Program, "u_lightDirection");
	g_colorLocation = glGetUniformLocation(g_program->Program, "u_color");

	g_vertexLocation = glGetAttribLocation(g_program->Program, "a_vertex");
	g_texCoordLocation = glGetAttribLocation(g_program->Program, "a_texCoord");

	// 创建一个方形的网格平面
	CreateRectangularGridPlane(&gridPlane, LENGTH, LENGTH, N - 1, N - 1, GL_FALSE);
	//获取该网格的索引总数
	g_numberIndices = gridPlane.numberIndices;

	// 将当前网格以X轴为基础旋转90度使其处于X-Z平面
	matrix = glm::rotate(matrix, glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
	for (i = 0; i < gridPlane.numberVertices; i++)
	{
		gridPlane.vertices[i] = matrix * gridPlane.vertices[i];
	}

	//存储顶点坐标的VBO
	glGenBuffers(1, &g_verticesVBO);
	glBindBuffer(GL_ARRAY_BUFFER, g_verticesVBO);
	glBufferData(GL_ARRAY_BUFFER, gridPlane.numberVertices * 4 * sizeof(GLfloat), (GLfloat*)gridPlane.vertices, GL_STATIC_DRAW);
	//存储顶点纹理坐标的VBO
	glGenBuffers(1, &g_texCoordsVBO);
	glBindBuffer(GL_ARRAY_BUFFER, g_texCoordsVBO);
	glBufferData(GL_ARRAY_BUFFER, gridPlane.numberVertices * 2 * sizeof(GLfloat), (GLfloat*)gridPlane.texCoords, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	//存储索引的VBO
	glGenBuffers(1, &g_indicesVBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_indicesVBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, gridPlane.numberIndices * sizeof(GLuint), (GLuint*)gridPlane.indices, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	ShapeDestroy(&gridPlane);

	//h0Data存储波浪模型的初始值
	h0Data = (GLfloat*)malloc(N * N * 2 * sizeof(GLfloat));
	if (!h0Data)
	{
		return GL_FALSE;
	}
	//Phillips频谱计算出初始波形
	windDirection = glm::normalize(windDirection);
	for (i = 0; i < N; i++)
	{
		waveDirection[1] = ((GLfloat)i - (GLfloat)N / 2.0f) * (2.0f * GL_PI / LENGTH);
		for (k = 0; k < N; k++)
		{
			waveDirection[0] = ((GLfloat)k - (GLfloat)N / 2.0f) * (2.0f * GL_PI / LENGTH);
			phillipsSpectrumValue = PhillipsSpectrum(AMPLITUDE, LPWA, waveDirection, windDirection);
			h0Data[i * 2 * N + k * 2 + 0] = 1.0f / sqrtf(2.0f) * RandomNormal(0.0f, 1.0f) * phillipsSpectrumValue;
			h0Data[i * 2 * N + k * 2 + 1] = 1.0f / sqrtf(2.0f) * RandomNormal(0.0f, 1.0f) * phillipsSpectrumValue;
		}
	}

	//将数据存储到一张贴图当中,g_textureH0贴图的R、G两个通道分别存储h0Data(相当于复数)的实部和虚部
	glGenTextures(1, &g_textureH0);
	glBindTexture(GL_TEXTURE_2D, g_textureH0);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, N, N, 0, GL_RG, GL_FLOAT, h0Data);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	free(h0Data);

	//用于存储波浪函数计算出来的复数值的贴图
	glGenTextures(1, &g_textureHt);
	glBindTexture(GL_TEXTURE_2D, g_textureHt);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, N, N, 0, GL_RG, GL_FLOAT, 0);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	//用于存储顶点偏移值的贴图(按行计算得到的值)
	glGenTextures(1, &g_textureDisplacement[0]);
	glBindTexture(GL_TEXTURE_2D, g_textureDisplacement[0]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, N, N, 0, GL_RG, GL_FLOAT, 0);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	//用于存储顶点偏移值的贴图(按列计算得到的值)
	glGenTextures(1, &g_textureDisplacement[1]);
	glBindTexture(GL_TEXTURE_2D, g_textureDisplacement[1]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, N, N, 0, GL_RG, GL_FLOAT, 0);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	//存储法线向量值
	glGenTextures(1, &g_textureNormal);
	glBindTexture(GL_TEXTURE_2D, g_textureNormal);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, N, N, 0, GL_RGBA, GL_FLOAT, 0);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glBindTexture(GL_TEXTURE_2D, 0);

	//生成逆傅里叶变换所需要的蝶形算法使用的索引值
	butterflyIndices = (GLint*)malloc(N * sizeof(GLint));
	if (!butterflyIndices)
	{
		return GL_FALSE;
	}
	//使用GPU计算FFT需要先把第一次迭代的索引计算好例:N=8时 索引为(0,4) (1,5) (2,6) (3,7)
	butterflyIndicesAsFloat = (GLfloat*)malloc(N * sizeof(GLfloat));
	if (!butterflyIndicesAsFloat)
	{
		free(butterflyIndices);
		return GL_FALSE;
	}
	for (i = 0; i < N; i++)
	{
		butterflyIndices[i] = i;
	}
	FourierButterflyShuffleFFTi(butterflyIndices, butterflyIndices, N);
	for (i = 0; i < N; i++)
	{
		butterflyIndicesAsFloat[i] = (GLfloat)butterflyIndices[i];
	}
	free(butterflyIndices);

	//将计算好的索引值存储到贴图当中
	glGenTextures(1, &g_textureIndices);
	glBindTexture(GL_TEXTURE_1D, g_textureIndices);
	glTexImage1D(GL_TEXTURE_1D, 0, GL_R32F, N, 0, GL_RED, GL_FLOAT, butterflyIndicesAsFloat);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glBindTexture(GL_TEXTURE_1D, 0);
	free(butterflyIndicesAsFloat);

	//将所需要的迭代次数传递到FFT的计算着色器中
	glUseProgram(g_computeFftProgram->Program);
	glUniform1i(g_stepsFftLocation, steps);

	//设置渲染使用的VAO
	glUseProgram(g_program->Program);
	glGenVertexArrays(1, &g_vao);
	glBindVertexArray(g_vao);
	glBindBuffer(GL_ARRAY_BUFFER, g_verticesVBO);
	glVertexAttribPointer(g_vertexLocation, 4, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(g_vertexLocation);
	glBindBuffer(GL_ARRAY_BUFFER, g_texCoordsVBO);
	glVertexAttribPointer(g_texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(g_texCoordLocation);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_indicesVBO);
	glBindVertexArray(0);

	lightDirection = glm::normalize(lightDirection);
	GLfloat HelpLight[3];
	HelpLight[0] = lightDirection.x;
	HelpLight[1] = lightDirection.y;
	HelpLight[2] = lightDirection.z;
	glUniform3fv(g_lightDirectionLocation, 1, HelpLight);
	GLfloat HelpColor[4];
	HelpColor[0] = color.x;
	HelpColor[1] = color.y;
	HelpColor[2] = color.z;
	HelpColor[3] = color.w;
	glUniform4fv(g_colorLocation, 1, HelpColor);
	glUseProgram(0);

	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);

	return GL_TRUE;
}

GLboolean OceanMeshForGPU::update(GLfloat time, glm::mat4 Model, glm::mat4 View, glm::mat4 Perspective, glm::vec3 CamPos, GLuint ReflectText)
{
	static GLfloat totalTime = 0.0f;

	//将所需要的三大变换矩阵传入到绘制用的着色器当中
	glUseProgram(g_program->Program);
	glm::mat3 normalMatrix = glm::mat3(Model);
	glUniformMatrix4fv(g_ModelLoc, 1, GL_FALSE, glm::value_ptr(Model));
	glUniformMatrix4fv(g_ViewLoc, 1, GL_FALSE, glm::value_ptr(View));
	glUniformMatrix4fv(g_ProjectionLoc, 1, GL_FALSE, glm::value_ptr(Perspective));
	glUniformMatrix3fv(g_normalMatrixLocation, 1, GL_FALSE, glm::value_ptr(normalMatrix));
	glUniform3fv(glGetUniformLocation(g_program->Program, "camPos"), 1, &CamPos[0]);
	glUseProgram(0);

	//使用Update计算着色器并绑定初始值贴图和即时值贴图
	glUseProgram(g_computeUpdateHtProgram->Program);
	glBindImageTexture(0, g_textureH0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RG32F);
	glBindImageTexture(1, g_textureHt, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RG32F);
	//传递运行时间
	glUniform1f(g_totalTimeUpdateHtLocation, totalTime);
	//创建一个N*N大小的工作组,即同时计算所有的顶点高度值
	glDispatchCompute(N, N, 1);
	//确保所有的数据都写入到贴图里了
	glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

	//使用FFT计算着色器对结果进行逆变换
	glUseProgram(g_computeFftProgram->Program);
	//绑定索引贴图、上个计算着色器所得结果的波浪函数贴图、将要存储偏移值的贴图
	glBindImageTexture(0, g_textureHt, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RG32F);
	glBindImageTexture(1, g_textureDisplacement[0], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RG32F);
	glBindImageTexture(2, g_textureIndices, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32F);
	glUniform1i(g_processColumnFftLocation, 0);
	// 先对每一行进行逆变换
	glDispatchCompute(1, N, 1);
	glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

	glBindImageTexture(0, g_textureDisplacement[0], 0, GL_FALSE, 0, GL_READ_ONLY, GL_RG32F);
	glBindImageTexture(1, g_textureDisplacement[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RG32F);
	glUniform1i(g_processColumnFftLocation, 1);
	// 再对每一列进行逆变换
	glDispatchCompute(1, N, 1);
	glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

	//更新法线向量
	glUseProgram(g_computeUpdateNormalProgram->Program);
	glBindImageTexture(0, g_textureDisplacement[1], 0, GL_FALSE, 0, GL_READ_ONLY, GL_RG32F);
	glBindImageTexture(1, g_textureNormal, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
	glDispatchCompute(N, N, 1);
	glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

	glUseProgram(g_program->Program);
	glBindVertexArray(g_vao);
	//将波浪高度贴图绑定在GL_TEXTURE0
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, g_textureDisplacement[1]);
	//将法线贴图绑定在GL_TEXTURE1
	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, g_textureNormal);

	glActiveTexture(GL_TEXTURE2);
	glBindTexture(GL_TEXTURE_2D, ReflectText);
	//根据索引开始绘制
	glDrawElements(GL_TRIANGLES, g_numberIndices, GL_UNSIGNED_INT, 0);
	//重置GL_TEXTURE0
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, 0);
	//重置GL_TEXTURE1
	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, 0);
	glBindVertexArray(0);

	totalTime += time;
	return GL_TRUE;
}
Update计算着色器代码:(基本上就是根据理论公式写代码)
#version 430 core

#define N 512
#define LENGTH	250.0
#define GRAVITY 9.81
#define GL_PI	3.1415926535897932384626433832795

layout (binding = 0, rg32f) uniform image2D u_imageIn; 
layout (binding = 1, rg32f) uniform image2D u_imageOut;

uniform float u_totalTime;

//使用布局限定符声明本地工作组大小为1*1*1
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

void main(void)
{
	//通过gl_GlobalInvocationID来得知当前执行单元在全局工作组中的位置
	ivec2 storePos = ivec2(int(gl_GlobalInvocationID.x), int(gl_GlobalInvocationID.y));
	ivec2 storePos_negative = ivec2(N - 1 - storePos.x, N - 1 - storePos.y);
	
	//根据位置storePos在贴图中采样得到数据
	vec2 h0 = imageLoad(u_imageIn, storePos).xy;
	vec2 h0_negative = imageLoad(u_imageIn, storePos_negative).xy;

	vec2 waveDirection;
	waveDirection.x = (float(-N) / 2.0 + gl_GlobalInvocationID.x) * (2.0 * GL_PI / LENGTH);
    waveDirection.y = (float(N) / 2.0 - gl_GlobalInvocationID.y) * (2.0 * GL_PI / LENGTH);

	float w2k = GRAVITY * length(waveDirection);

	float wktime = sqrt(w2k) * u_totalTime;

	float cos_wktime = cos(wktime);
	float sin_wktime = sin(wktime);

	vec2 ht;
	ht.x = (h0.x * cos_wktime - h0.y * sin_wktime) + (h0_negative.x * cos_wktime - h0_negative.y * sin_wktime); 
	ht.y = (h0.x * sin_wktime + h0.y * cos_wktime) + (h0_negative.x * sin_wktime + h0_negative.y * cos_wktime); 
	//将算出来的高度值存储到贴图当中
	imageStore(u_imageOut, storePos, vec4(ht, 0.0, 0.0));
}
FFT计算着色器代码:

#version 430 core

#define N 512
#define GL_PI	3.1415926535897932384626433832795

uniform int u_processColumn;

uniform int u_steps;

layout (binding = 0, rg32f) uniform image2D u_imageIn; 
layout (binding = 1, rg32f) uniform image2D u_imageOut;

layout (binding = 2, r32f) uniform image1D u_imageIndices;

// 如果一个变量被声明为shared,那么它将被保存到特定的位置,从而对同一个本地工作组内的所有计算着色器请求可见,通常访问共享shared变量的性能会远远好于访问图像或者着色器存储缓存(例如主内存)的性能
shared vec2 sharedStore[N];

// as N = 512, so local size is 512/2 = 256. Processing two fields per invocation.
layout (local_size_x = 256, local_size_y = 1, local_size_z = 1) in;

//复数乘法
vec2 mulc(vec2 a, vec2 b)
{
	vec2 result;
	
	result.x = a.x * b.x - a.y * b.y;
	result.y = a.x * b.y + b.x * a.y;
	
	return result;
}

//转换成单位根向量
vec2 rootOfUnityc(int n, int k)
{
	vec2 result;
	
	result.x = cos(2.0 * GL_PI * float(k) / float(n));
	result.y = sin(2.0 * GL_PI * float(k) / float(n));

	return result;
}

void main(void)
{
	ivec2 leftStorePos;
	ivec2 rightStorePos;

	ivec2 leftLoadPos;
	ivec2 rightLoadPos;

	int xIndex = int(gl_GlobalInvocationID.x);
	int yIndex = int(gl_GlobalInvocationID.y);

	int leftStoreIndex = 2 * xIndex;
	int rightStoreIndex = 2 * xIndex + 1;

	//读取索引(每一组有两个索引例如(0,4))
	int leftLoadIndex = int(imageLoad(u_imageIndices, leftStoreIndex).r);
	int rightLoadIndex = int(imageLoad(u_imageIndices, rightStoreIndex).r);

	// 加载和存储位置取决于行或列。
	if (u_processColumn == 0)
	{
		leftLoadPos = ivec2(leftLoadIndex, yIndex);
		rightLoadPos = ivec2(rightLoadIndex, yIndex);

		leftStorePos = ivec2(leftStoreIndex, yIndex);
		rightStorePos = ivec2(rightStoreIndex, yIndex);
	}
	else
	{
		leftLoadPos = ivec2(yIndex, leftLoadIndex);
		rightLoadPos = ivec2(yIndex, rightLoadIndex);

		leftStorePos = ivec2(yIndex, leftStoreIndex);
		rightStorePos = ivec2(yIndex, rightStoreIndex);
	}

	// 从贴图中读取数据
	vec2 leftValue = imageLoad(u_imageIn, leftLoadPos).xy;
	vec2 rightValue = imageLoad(u_imageIn, rightLoadPos).xy;
	//放入到共享缓存中
	sharedStore[leftStoreIndex] = leftValue;
	sharedStore[rightStoreIndex] = rightValue;

	//确保所有数据都存储完毕(否则后续逻辑将无法读到所需的数据,即要保证时序)
	memoryBarrierShared();
	barrier();
	
	
	int numberSections = N / 2;
	int numberButterfliesInSection = 1;

	int currentSection = xIndex;
	int currentButterfly = 0;

	// 计算FFT
	for (int currentStep = 0; currentStep < u_steps; currentStep++)
	{	
		//根据位置来获取该组所需的两个索引
		int leftIndex = currentButterfly + currentSection * numberButterfliesInSection * 2;
		int rightIndex = currentButterfly + numberButterfliesInSection + currentSection * numberButterfliesInSection * 2;
		//从共享缓存中获得数据
		leftValue = sharedStore[leftIndex];
		rightValue = sharedStore[rightIndex];
			 						
		vec2 currentW = rootOfUnityc(numberButterfliesInSection * 2, currentButterfly);
	
		vec2 multiply;
		vec2 addition;
		vec2 subtraction;

		multiply = mulc(currentW, rightValue);	
		
		addition = leftValue + multiply;
		subtraction = leftValue - multiply; 

		sharedStore[leftIndex] = addition;
		sharedStore[rightIndex] = subtraction;		

		// 确保所有数据计算并存储完毕	
		memoryBarrierShared();

		// 根据蝴蝶算法来改变参数	
		numberButterfliesInSection *= 2;
		numberSections /= 2;

		currentSection /= 2;
		currentButterfly = xIndex % numberButterfliesInSection;

		// 确保所有的计算着色器都计算完毕
		barrier();
	}
	
	if (u_processColumn == 1)
	{
		if ((leftStorePos.x + leftStorePos.y) % 2 == 0)
		{
			sharedStore[leftStoreIndex] *= -1.0;
		}
		if ((rightStorePos.x + rightStorePos.y) % 2 == 0)
		{
			sharedStore[rightStoreIndex] *= -1.0;			
		}
		
		memoryBarrierShared();
	}
	
	imageStore(u_imageOut, leftStorePos, vec4(sharedStore[leftStoreIndex], 0.0, 0.0));
	imageStore(u_imageOut, rightStorePos, vec4(sharedStore[rightStoreIndex], 0.0, 0.0));
}
法线计算着色器:(水平、竖直、对角线分别采样并计算切线空间的B、T向量来计算法线)
#version 430 core

#define N 512
#define LENGTH	250.0

#define VERTEX_STEP (LENGTH / float(N - 1))
#define DIAGONAL_VERTEX_STEP sqrt(VERTEX_STEP * VERTEX_STEP * 2.0)

layout (binding = 0, rg32f) uniform image2D u_imageIn; 
layout (binding = 1, rgba32f) uniform image2D u_imageOut;

layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

float sampleImage(ivec2 pos)
{
	ivec2 cpos = clamp(pos, 0, N - 1);
	
	return imageLoad(u_imageIn, cpos).r;
}

vec3 calculateNormal(ivec2 texCoord)
{
	vec3 normal = vec3(0.0f, 0.0f, 0.0f);

	ivec2 right = ivec2(texCoord.s + 1, texCoord.t);
	ivec2 top = ivec2(texCoord.s, texCoord.t + 1);
	ivec2 left = ivec2(texCoord.s - 1, texCoord.t);
	ivec2 bottom = ivec2(texCoord.s, texCoord.t - 1);

	float slopeHorizontal = sampleImage(right) - sampleImage(left);
	float slopeVertical = sampleImage(top) - sampleImage(bottom);
	
	vec3 tangent = normalize(vec3(2.0 * VERTEX_STEP, slopeHorizontal, 0.0));
	vec3 bitangent = normalize(vec3(0.0, slopeVertical, -2.0 * VERTEX_STEP));
	
	normal += normalize(cross(tangent, bitangent));
	
	ivec2 rightTop = ivec2(texCoord.s + 1, texCoord.t + 1);
	ivec2 leftTop = ivec2(texCoord.s - 1, texCoord.t + 1);
	ivec2 leftBottom = ivec2(texCoord.s - 1, texCoord.t - 1);
	ivec2 rightBottom = ivec2(texCoord.s + 1, texCoord.t - 1);
	
	float slopeDown = sampleImage(rightBottom) - sampleImage(leftTop);
	float slopeUp = sampleImage(rightTop) - sampleImage(leftBottom);

	tangent = normalize(vec3(2.0 * DIAGONAL_VERTEX_STEP, slopeDown, 2.0 * DIAGONAL_VERTEX_STEP));
	bitangent = normalize(vec3(2.0 * DIAGONAL_VERTEX_STEP, slopeUp, -2.0 * DIAGONAL_VERTEX_STEP));

	normal += normalize(cross(tangent, bitangent));
		
	return normalize(normal);
}

void main(void)
{
	ivec2 storePos = ivec2(int(gl_GlobalInvocationID.x), int(gl_GlobalInvocationID.y));

	vec3 normal = calculateNormal(storePos);

	imageStore(u_imageOut, storePos, vec4(normal, 0.0));
}
竟会的到下面的结果(注:我没有对顶点进行水平扰动)


二、水面渲染

水面的渲染最主要在于反射效果和折射效果,由于我并没有实现折射效果,所以只会在这里简单说一下反射效果,详见: 实时渲染的水特效
其主要思路是将场景内所有要渲染的物体乘上一个reflection matrix使其根据反射面倒置,然后将这些渲染到一张贴图当中(要使用帧缓冲),然后在将这张帖图绑在水平面模型上即可。
大体代码:
glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Texture, 0);
glViewport(0, 0, 512, 512);
ThisSky.Render(ThisView, ThisProjection, GPUOcean.GetReflectMatrix(), 1);

glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, screenWidth, screenHeight);

GPUOcean.update(deltaTime, ThisModel, ThisView, ThisProjection, camera.Position, Texture);
ThisSky.Render(ThisView, ThisProjection, GPUOcean.GetReflectMatrix(), 0);
vertexshader:
#version 430 core


layout(binding = 0) uniform sampler2D u_displacementMap;


uniform mat4 u_Model;
uniform mat4 u_View;
uniform mat4 u_Projection;

in vec4 a_vertex;
in vec2 a_texCoord;

out vec2 v_texCoord;
out vec3 WorldPos;
out vec4 vReflectCoordinates;

void main(void)
{
    v_texCoord = a_texCoord;
    vec4 displacement = vec4(0.0, texture(u_displacementMap, a_texCoord).r, 0.0, 0.0);
    WorldPos = vec3(u_Model * (a_vertex + displacement));
    gl_Position = u_Projection * u_View * vec4(WorldPos, 1.0f);
    vReflectCoordinates = gl_Position;
    //vReflectCoordinates = u_Projection * u_View * u_Model * u_ReflectMatrix * (a_vertex + displacement);
}

fragmentshader:
#version 430 core

layout(binding = 1) uniform sampler2D u_normalMap;
layout(binding = 2) uniform sampler2D ReflectionText;

uniform mat3 u_normalMatrix;
uniform vec3 u_lightDirection;
uniform vec4 u_color;
uniform vec3 camPos;

in vec2 v_texCoord;
in vec3 WorldPos;
in vec4 vReflectCoordinates;

out vec4 fragColor;

float u_exposure = 0.5;

vec3 hdr (vec3 color, float exposure) 
{
	return 1.0 - exp(-color * exposure);
}
		
void main (void)
{
	vec3 normal = texture2D( u_normalMap, v_texCoord ).rgb;

	vec3 view = normalize( camPos - WorldPos );
			
	vec3 R = normalize( reflect( -view, normalize(vec3(0.0f, 1.0f, 0.0f)) ) );

	vec3 reflection = normalize( reflect( -u_lightDirection, normal ) );
	
	float specularFactor = pow( max( 0.0, dot( view, reflection ) ), 500.0 ) * 200.0;
		
	vec3 distortion = normal * vec3( 0.1, 0.0, 0.1 ) ;
	//vec3 reflectionColor = texture2DProj( ReflectionText, vReflectCoordinates.xyz + distortion ).xyz;
	//vec3 reflectionColor = vec3( 0.1f, 0.1f, 0.1f ); 
	//vec3 reflectionColor = vec3( texture(skybox, v_texCoord + distortion.xz) ) * 0.4f; 
	vec2 texf = vec2( vReflectCoordinates.x, vReflectCoordinates.y ) / vReflectCoordinates.w * 0.5 + 0.5 + distortion.xz;
    vec3 reflectionColor = vec3(texture( ReflectionText, texf )) * 0.3f;

	float distanceRatio = min( 1.0, log( 1.0 / length( camPos - WorldPos ) * 3000.0 + 1.0 ) );
	distanceRatio *= distanceRatio;
	distanceRatio = distanceRatio * 0.7 + 0.3;
			
	normal = ( distanceRatio * normal + vec3( 0.0, 1.0 - distanceRatio, 0.0 ) ) * 0.5;
	normal /= length( normal );
			
	float fresnel = pow( 1.0 - dot( normal, view ), 2.0 );
			
	float skyFactor = ( fresnel + 0.2 ) * 10.0;
	vec3 waterColor = ( 1.0 - fresnel ) * vec3( 0.004, 0.016, 0.047 );
			
	vec3 color = ( skyFactor + specularFactor + waterColor ) * reflectionColor + waterColor * 0.5 ;
	color = hdr( color, u_exposure );

	fragColor = vec4( color, 1.0 );
}




 
  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值