OpenGL超级宝典 渲染管线(二)

细分曲面着色器

细分曲面分成了三部分,
首先是细分曲面控制着色器,这个着色器主要是设置细分因子,也就是细分的程度。
然后是固定的细分曲面引擎,将接收到的贴片(patch)细分成点、线或者三角形
最后是细分曲面评估着色器,这里进行细分算法的编写,并且输出

自己封装的shader类(改进中…)

shader.h

#pragma once
#include "sb7.h"
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <exception>
class Shader
{
public:
	Shader();
	Shader(const std::string vertexPath, const std::string fragmentPath);
	Shader(const std::string vertexPath, const std::string tescPath, const std::string tesePath, const std::string fragmentPath);

	//设置顶点着色器
	void setVertexShder(const std::string vertexPath);
	//设置曲面细分控制着色器
	void setTescShader(const std::string tescPath);
	//设置曲面细分评估着色器
	void setTeseShader(const std::string tesePath);
	//设置片段着色器
	void setFragmentShader(const std::string fragmentPath);


	//program对象使用
	void use();
private:
	GLuint program;

	//源代码
	std::string shaderString;
	const char* shaderSource;
	//用于读取shader文件
	std::ifstream shaderFile;
	//转换源码
	std::stringstream shaderSStream;

private:
	//查错程序,是否有编译错误
	void checkCompileErrors(unsigned int ID, std::string type);
};

Shader.cpp

#include "Shader.h"

Shader::Shader(){}

Shader::Shader(const std::string vertexPath, const std::string fragmentPath)
{
	//创建程序对象
	program = glCreateProgram();

	setVertexShder(vertexPath);
	setFragmentShader(fragmentPath);
	
}

Shader::Shader(const std::string vertexPath, const std::string tescPath, const std::string tesePath, const std::string fragmentPath)
{
	//创建程序对象
	program = glCreateProgram();

	setVertexShder(vertexPath);
	setFragmentShader(fragmentPath);
	setTescShader(tescPath);
	setTeseShader(tesePath);
}

//设置顶点着色器
void Shader::setVertexShder(const std::string vertexPath)
{
	//将上一个shader的源码清理
	shaderSStream.str("");
	memset(&shaderSource, '\0', sizeof(shaderSource));
	//vertex shader
	if (!vertexPath.empty())	//判断文件路径是否为空
	{
		//打开文件
		shaderFile.open(vertexPath);
		//检测文件是否有误
		shaderFile.exceptions(std::ifstream::badbit || std::ifstream::failbit);

		try
		{
			if (!shaderFile.is_open())	//判断文件是否打开
			{
				throw std::exception("open vertex shader file fail");
			}
			//读取源代码到字符串流
			shaderSStream << shaderFile.rdbuf();
			//读取完毕,可以关闭文件了
			shaderFile.close();
			//将源代码输入到字符串中去
			shaderString = shaderSStream.str();
			//由于读取的是const char*类型的,所以用一个string转换一下
			shaderSource = shaderString.c_str();

			//创建顶点着色器对象
			GLuint vertexShader;
			vertexShader = glCreateShader(GL_VERTEX_SHADER);
			//将shader的源代码传递给shader对象
			glShaderSource(vertexShader, 1, &shaderSource, NULL);
			//编译着色器中的源代码
			glCompileShader(vertexShader);
			//将shader对象附加到program对象上去
			glAttachShader(program, vertexShader);
			//连接program对象上的shader对象
			glLinkProgram(program);
			//查错
			checkCompileErrors(program, "VERTEX");
			//可以把已经附加并且链接好的shader对象删除了
			glDeleteShader(vertexShader);
		}
		catch (const std::exception& e)
		{
			std::cout << e.what() << std::endl;
		}

	}
}

//设置曲面细分控制着色器
void Shader::setTescShader(const std::string tescPath)
{
	//将上一个shader的源码清理
	shaderSStream.str("");
	memset(&shaderSource, '\0', sizeof(shaderSource));
	//fragment shader
	if (!tescPath.empty())	//判断文件路径是否为空
	{
		//文件路径不为空,打开文件
		shaderFile.open(tescPath);
		shaderFile.exceptions(std::ifstream::badbit || std::ifstream::failbit);

		try
		{
			if (!shaderFile.is_open())	//文件打开失败
			{
				throw std::exception("open tesc shader file fail");
			}
			//文件成功打开
			//字符串流读取文件中的数据
			shaderSStream << shaderFile.rdbuf();
			//读完了,把文件关了
			shaderFile.close();
			//把源代码读入字符串中去
			shaderString = shaderSStream.str();
			//把string转成系数需要的const char *类型
			shaderSource = shaderString.c_str();
			//创建shader对象
			GLuint tescShader;
			tescShader = glCreateShader(GL_TESS_CONTROL_SHADER);
			//将源代码读入shader对象
			glShaderSource(tescShader, 1, &shaderSource, NULL);
			//编译shader对象中的源码
			glCompileShader(tescShader);
			//将shader对象附加到program对象上去
			glAttachShader(program, tescShader);
			//链接program对象上的shader对象
			glLinkProgram(program);
			//查错
			checkCompileErrors(program, "PROGRAM");
			//完事,可以把shader对象删了
			glDeleteShader(tescShader);
		}
		catch (const std::exception& e)
		{
			std::cout << e.what() << std::endl;
		}
	}
}

//设置曲面细分评估着色器
void Shader::setTeseShader(const std::string tesePath)
{
	//将上一个shader的源码清理
	shaderSStream.str("");
	memset(&shaderSource, '\0', sizeof(shaderSource));
	//fragment shader
	if (!tesePath.empty())	//判断文件路径是否为空
	{
		//文件路径不为空,打开文件
		shaderFile.open(tesePath);
		shaderFile.exceptions(std::ifstream::badbit || std::ifstream::failbit);

		try
		{
			if (!shaderFile.is_open())	//文件打开失败
			{
				throw std::exception("open tese shader file fail");
			}
			//文件成功打开
			//字符串流读取文件中的数据
			shaderSStream << shaderFile.rdbuf();
			//读完了,把文件关了
			shaderFile.close();
			//把源代码读入字符串中去
			shaderString = shaderSStream.str();
			//把string转成系数需要的const char *类型
			shaderSource = shaderString.c_str();

			//创建shader对象
			GLuint teseShader;
			teseShader = glCreateShader(GL_TESS_EVALUATION_SHADER);
			//将源代码读入shader对象
			glShaderSource(teseShader, 1, &shaderSource, NULL);
			//编译shader对象中的源码
			glCompileShader(teseShader);
			//将shader对象附加到program对象上去
			glAttachShader(program, teseShader);
			//链接program对象上的shader对象
			glLinkProgram(program);
			//查错
			checkCompileErrors(program, "PROGRAM");
			//完事,可以把shader对象删了
			glDeleteShader(teseShader);
		}
		catch (const std::exception& e)
		{
			std::cout << e.what() << std::endl;
		}
	}
}

//设置片段着色器
void Shader::setFragmentShader(const std::string fragmentPath)
{
	//将上一个shader的源码清理
	shaderSStream.str("");
	memset(&shaderSource, '\0', sizeof(shaderSource));
	//fragment shader
	if (!fragmentPath.empty())	//判断文件路径是否为空
	{
		//文件路径不为空,打开文件
		shaderFile.open(fragmentPath);
		shaderFile.exceptions(std::ifstream::badbit || std::ifstream::failbit);

		try
		{
			if (!shaderFile.is_open())	//文件打开失败
			{
				throw std::exception("open fragment shader file fail");
			}
			//文件成功打开
			//字符串流读取文件中的数据
			shaderSStream << shaderFile.rdbuf();
			//读完了,把文件关了
			shaderFile.close();
			//把源代码读入字符串中去
			shaderString = shaderSStream.str();
			//把string转成系数需要的const char *类型
			shaderSource = shaderString.c_str();
			//创建shader对象
			GLuint fragmentShader;
			fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
			//将源代码读入shader对象
			glShaderSource(fragmentShader, 1, &shaderSource, NULL);
			//编译shader对象中的源码
			glCompileShader(fragmentShader);
			//将shader对象附加到program对象上去
			glAttachShader(program, fragmentShader);
			//链接program对象上的shader对象
			glLinkProgram(program);
			//查错
			checkCompileErrors(program, "PROGRAM");
			//完事,可以把shader对象删了
			glDeleteShader(fragmentShader);
		}
		catch (const std::exception& e)
		{
			std::cout << e.what() << std::endl;
		}
	}
}

void Shader::use()
{
	glUseProgram(program);
}


/*
* 查错
*/
void Shader::checkCompileErrors(unsigned int ID, std::string type)
{
	int success;
	char infoLog[512];

	if (type != "PROGRAM")
	{
		glGetShaderiv(ID, GL_COMPILE_STATUS, &success);
		if (!success)
		{
			glGetShaderInfoLog(ID, 512, NULL, infoLog);
			std::cout << "shader compile error: " << infoLog << std::endl;
		}
	}
	else
	{
		glGetProgramiv(ID, GL_LINK_STATUS, &success);
		if (!success)
		{
			glGetProgramInfoLog(ID, 512, NULL, infoLog);
			std::cout << "program linking error: " << infoLog << std::endl;
		}
	}
}

shader

vertex shader

#version 450 core
//VAO的0个位置的数据
layout(location = 0) in vec4 offset;
layout(location = 1) in vec4 color;

void main()
{
	const vec4 vertices[3] = vec4[3](vec4(0.25, -0.25, 0.5, 1.0),
	vec4(-0.25, -0.25, 0.5, 1.0),
	vec4(0.25, 0.25, 0.5, 1.0));

	gl_Position = vertices[gl_VertexID] + offset;
}
  • 相较于上一次的shader,没有啥变化

tess control shader(细分曲面控制着色器)

#version 450 core
//将输出顶点设置为3个
layout(vertices = 3) out;

void main()
{
	if(gl_InvocationID == 0)
	{
		//将细分因子写入这两个变量中,后面会详细讲什么意思
		gl_TessLevelInner[0] = 5.0;
		gl_TessLevelOuter[0] = 5.0;
		gl_TessLevelOuter[1] = 5.0;
		gl_TessLevelOuter[2] = 5.0;
	}
	//将输入的数据作为输出数据
	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
}

tess evaluation shader(细分曲面评估着色器)

#version 450 core
//模式选择为三角形,顺时针为正面,
layout(triangles, equal_spacing, cw) in;
void main()
{
	//细分曲面单元生成的顶点重心坐标,gl_in是曲面细分控制着色器传进来的gl_out
	gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position + 
				gl_TessCoord.y * gl_in[1].gl_Position + 
				gl_TessCoord.z * gl_in[2].gl_Position);
}

fragment shader

#version 450 core

out vec4 color;

void main()
{
	color = vec4(1.0, 1.0, 1.0, 1.0);
}

源码

#include "sb7.h"
#include "Shader.h"
#include <math.h>
class my_application : public sb7::application
{
	void startup()
	{
		shader = new Shader("vertexShader.vert", "tcsShader.tesc", "tesShader.tese", "fragShader.frag");;

		//创建数组对象
		glCreateVertexArrays(2, vertex_arrays_object);
		//绑定array对象到OpenGL上下文,让OpenGL知道这些数据
		glBindVertexArray(vertex_arrays_object[0]);
		glBindVertexArray(vertex_arrays_object[1]);
	}

	void render(double currentTime)
	{
		const GLfloat color[] = {
			 0.0f + 0.0f,
			 0.0f + 0.0f, 0.0f, 1.0f
		};
		glClearBufferfv(GL_COLOR, 0, color);

		shader->use();

		GLfloat attrib[]{
			0.0f,
			0.0f, 0.0, 0.0f
		};

		GLfloat triangleColor[] = {
			 1.0f,
			 1.0f, 1.0f, 1.0f
		};

		glVertexAttrib4fv(0, attrib);
		glVertexAttrib4fv(1, triangleColor);

		//贴片的控制单数量设置
		glPatchParameteri(GL_PATCH_VERTICES, 3);
		//仅绘制三角形的轮廓
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

		glDrawArrays(GL_PATCHES, 0, 3);
	}

	void shutdown() 
	{

	}

private:
	Shader* shader;
	//顶点数组对象
	GLuint vertex_arrays_object[2];
};


DECLARE_MAIN(my_application);

出现过的问题

传入的基元与着色器中的不一致导致的错误

错误信息:
GL_INVALID_OPERATION error generated. State(s) are invalid: primitive mode match.
导致的后果:
无法绘制出图形
解决方案:
使用了曲面细分着色器时,查看glDrawArraws(GLenum mode, GLuint first, GLuint count)函数的第一个参数是否GL_PATCHS,如果不是的话,就会产生上述的问题。

stringstream清空的问题

在我自己封装的Shader类中,只使用了一个stringstream读取shader文件,所以每次使用之前要避免上一个shader的数据还存在流中,就需要清空流
使用clear()函数,无法达到清空流的效果
需要使用str("")才能成功的清空当前的stringstream

效果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ht巷子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值