openGL学习(Shader)

认识Shader

在计算机图形学中,Shader(着色器)是一种运行在 GPU(图形处理单元)上的程序,用于控制图形渲染过程中顶点和像素的处理。着色器是 OpenGL、Direct3D、Vulkan 等图形 API 的核心组成部分,它们允许开发者自定义渲染管线的各个阶段,从而实现高度自定义的视觉效果。

着色器类型

  1. 顶点着色器(Vertex Shader)

    • 处理每个顶点的数据。

    • 可以进行顶点位置的变换、纹理坐标的生成、光照计算等。

    • 通常用于实现 3D 变换、动画效果等。

  2. 片段着色器(Fragment Shader)

    • 处理每个像素(或片段)的数据。

    • 决定每个像素的颜色和透明度。

    • 用于实现光照、阴影、纹理映射、后处理效果等。

  3. 几何着色器(Geometry Shader)

    • 在顶点着色器和片段着色器之间处理整个图元(如点、线、三角形)。

    • 可以生成新的顶点或图元。

    • 用于实现高级的几何变换和效果。

  4. 张量着色器(Tessellation Shader)

    • 包括两个阶段:域着色器(Domain Shader)和原语生成着色器(Hull Shader)。

    • 控制细分曲面的生成和处理。

    • 用于实现复杂的曲面细分和建模。

  5. 计算着色器(Compute Shader)

    • 执行通用计算任务,不直接参与渲染管线。

    • 可以并行处理大量数据。

    • 用于实现物理模拟、图像处理、AI 计算等。

着色器编写

着色器通常使用 GLSL(OpenGL Shading Language)或 HLSL(High-Level Shader Language)等语言编写。以下是一个简单的 GLSL 顶点着色器示例:

#version 330 core

// in代表输入 
//vec3代表是一个三维向量(xyz)
// aPos 是我们自己取的名字
// layout(location = n) 代表告诉vertexShaser去vao的第n个属性描述中去取数据。
// gl_Position 是glsl的内置变量,负责向后续阶段输出顶点位置处理的结果。 一般为NDC坐标。
//vec4代表 四维向量。

layout(location = 0) in vec3 aPos;  // 顶点位置

void main() {
    gl_Position = vec4(aPos, 1.0);  // 将顶点位置传递给片段着色器
}

以及一个简单的 GLSL 片段着色器示例:

#version 330 core

//out代表输出变量
//vec4代表四维向量,rgba,红绿蓝,透明度
//FragColor 是最终输出的变量
out vec4 FragColor;  // 输出颜色

void main() {
    FragColor = vec4(1.0, 0.5, 0.2, 1.0);  // 设置像素颜色为橙色
}

着色器编译和链接

在 OpenGL 中,着色器需要经过编译和链接才能使用。以下是一个简化的流程:

  1. 编写着色器代码:使用 GLSL 编写顶点着色器和片段着色器代码。

  2. 创建着色器对象:使用 glCreateShader 创建着色器对象。

  3. 编译着色器:使用 glShaderSourceglCompileShader 编译着色器代码。

  4. 创建程序对象:使用 glCreateProgram 创建程序对象。

  5. 附加着色器:使用 glAttachShader 将编译好的着色器附加到程序对象。

  6. 链接程序:使用 glLinkProgram 链接程序对象。

  7. 使用程序:使用 glUseProgram 使用链接好的程序对象。

着色器的优势

  1. 高度自定义:开发者可以自定义渲染管线的各个阶段,实现复杂的图形效果。

  2. 性能优化:着色器在 GPU 上运行,可以利用 GPU 的并行处理能力,提高渲染性能。

  3. 跨平台兼容性:着色器语言(如 GLSL)在不同的图形 API 和硬件平台上具有较好的兼容性。

总之,着色器是现代图形渲染中不可或缺的一部分,它们为开发者提供了强大的工具来实现高度自定义的视觉效果。

void  prepareShader()
{
	cout << "prepareShader()" << endl;
	//1.完成vs和fs,并且装字符串
	const char* vertexShaderSource =
		"#version 460 core\n"
		"layout(location = 0) in vec3 aPos;\n"
		"void main()\n"
		"{\n"
		"gl_Position = vec4(aPos.x,aPos.y,aPos.z,1.0);\n"
		"}\n\0";

	const char* fragmentShaderSource =
		"#version 330 core\n"
		"out vec4 FragColor;\n"
		"void main()\n"
		"{\n"
		"FragColor = vec4(1.0f,0.5f,0.2f,1.0);\n"
		"}\n\0";
	//2创建Shader程序(vs和fs)
	GLuint vertex, fragment;
	vertex = glCreateShader(GL_VERTEX_SHADER);
	fragment = glCreateShader(GL_FRAGMENT_SHADER);

	//3为shader程序输入Shader代码
	glShaderSource(vertex, 1, &vertexShaderSource, NULL); //字符串用\0进行结尾,不需要告诉他长度。
	glShaderSource(fragment, 1, &fragmentShaderSource, NULL); //字符串用\0进行结尾,不需要告诉他长度。
	int success = 0;
	char infoLog[1024];
	//4 执行Shader编译
	glCompileShader(vertex);
	//5 检查是否正确编译
	glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
	if (!success) // 0有问题,非0没有问题
	{
		glGetShaderInfoLog(vertex, 1024, NULL, infoLog); //获取日志信息
		cout << "Error Vertex Shaser Complie: " << infoLog << endl;
	}
	//4 执行fragment编译
	glCompileShader(fragment);
	//5 检查是否正确编译
	glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
	if (!success) // 0有问题,非0没有问题
	{
		glGetShaderInfoLog(vertex, 1024, NULL, infoLog); //获取日志信息
		cout << "Error Fragment Shader Complie: " << infoLog << endl;
	}

	// 创建Program壳子
	GLuint program = 0;
	program =	glCreateProgram();
	//将编译后的结果放进Program
	glAttachShader(program, vertex);
	glAttachShader(program, fragment);

	// 执行program链接操作,形成最终的shader程序。
	glLinkProgram(program);
	//检查链接错误
	glGetProgramiv(program, GL_LINK_STATUS, &success);
	if (!success) // 0有问题,非0没有问题
	{
		glGetProgramInfoLog(program, 1024, NULL, infoLog); //获取日志信息
		cout << "Error Program Link: " << infoLog << endl;
	}
	//清理
	glDeleteShader(vertex);
	glDeleteShader(fragment);

}
#include <iostream>
//#include "thrirdParty/include/GLFW/glfw3.h" 
#include "glad/glad.h"   //需要先引用glad的头文件。 用于加载 OpenGL 函数指针
#include "GLFW/glfw3.h" // 用于创建窗口和处理输入。	
#include <assert.h>
#include "wrapper/checkerror.h"
#include "application/application.h"
using namespace std;

/*
* 目标:学习DrawArray进行绘制
* 1.采用GL_TRANGLES进行绘制三角形
* 这里可以缩放窗体,实验NDC坐标的作用。
* prepareVAOForGLTriangles:构建四个顶点的VAO
*  
*/

GLuint vao, program;

void prepareSingleBuffer()
{
	//1.准备顶点位置数据与颜色数据
	float positions[] = {
		-0.5f,-0.5f,0.0f,
		0.5f,-0.5f,0.0f,
		0.0f,0.5f,0.0f,
	};
	float colors[] = {
		1.0f,0.0f,0.0f,
		0.0f,1.0f,0.0f,
		0.0f,0.0f,1.0f,
	};

	//2.为位置&颜色数据各自生成一个VBO
	GLuint posVbo = 0, colorVbo = 0;
	GL_CALL(glGenBuffers(1, &posVbo));
	GL_CALL(glGenBuffers(1, &colorVbo));
	
	//3.给两个分开的VBO各自填充数据
	//positions填充数据
	GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, posVbo));
	GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW));

	//colors填充数据
	GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, colorVbo));
	GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW));
	
	//生成VAO并且绑定
	GLuint vao = 0;
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	// 分别将数据放入VAO
	//描述位置属性
	glBindBuffer(GL_ARRAY_BUFFER, posVbo);//只有绑定了vbo,下面的属性描述才于此有关系
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3*sizeof(float),0);

	glBindBuffer(GL_ARRAY_BUFFER, colorVbo);//只有绑定了vbo,下面的属性描述才于此有关系
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
	
	glBindVertexArray(0); //将VAO进行解绑

}

void prepareInterLeavedBuffer()
{
	cout << "prepareInterLeavedBuffer()" << endl;

	float vertices[] = {
	-0.5f,-0.5f,0.0f,1.0f,0.0f,0.0f,
	0.5f,-0.5f,0.0f,0.0f,1.0f,0.0f,
	0.0f,0.5f,0.0f,0.0f,0.0f,1.0f
	};

	//2.为位置&颜色数据各自生成一个VBO
	GLuint vbo = 0;
	GL_CALL(glGenBuffers(1, &vbo));
	GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
	GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));

	//生成VAO并且绑定
	//GLuint vao = 0;
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	// 分别将数据放入VAO
	//描述位置属性
	glBindBuffer(GL_ARRAY_BUFFER, vbo);//只有绑定了vbo,下面的属性描述才于此有关系
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	
	//颜色属性
	//glBindBuffer(GL_ARRAY_BUFFER, vbo);//只有绑定了vbo,下面的属性描述才于此有关系
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),(void*)(3*sizeof(float)));
	glBindVertexArray(0); //将VAO进行解绑
}


void prepareVAOForTriangles()
{
	cout << "prepareVAOForTriangles()" << endl;

	//目前暂时不用颜色
	float vertices[] = {
	-0.5f,-0.5f,0.0f,
	0.5f,-0.5f,0.0f,
	0.0f,0.5f,0.0f,
	0.5f,0.5f,0.0f,
	0.8f,0.8f,0.0f,
	0.8f,0.0f,0.0f
		};

	//2.生成一个VBO
	GLuint vbo = 0;
	GL_CALL(glGenBuffers(1, &vbo));
	GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
	GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));

	//生成VAO并且绑定
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	// 分别将数据放入VAO
	//描述位置属性
	glBindBuffer(GL_ARRAY_BUFFER, vbo);//只有绑定了vbo,下面的属性描述才于此有关系
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

	glBindVertexArray(0); //将VAO进行解绑
}


void  prepareShader()
{
	cout << "prepareShader()" << endl;
	//1.完成vs和fs,并且装字符串
	const char* vertexShaderSource =
		"#version 460 core\n"
		"layout(location = 0) in vec3 aPos;\n"
		"void main()\n"
		"{\n"
		"gl_Position = vec4(aPos.x,aPos.y,aPos.z,1.0);\n"
		"}\n\0";

	const char* fragmentShaderSource =
		"#version 330 core\n"
		"out vec4 FragColor;\n"
		"void main()\n"
		"{\n"
		"FragColor = vec4(1.0f,0.5f,0.2f,1.0);\n"
		"}\n\0";
	//2创建Shader程序(vs和fs)
	GLuint vertex, fragment;
	vertex = glCreateShader(GL_VERTEX_SHADER);
	fragment = glCreateShader(GL_FRAGMENT_SHADER);

	//3为shader程序输入Shader代码
	glShaderSource(vertex, 1, &vertexShaderSource, NULL); //字符串用\0进行结尾,不需要告诉他长度。
	glShaderSource(fragment, 1, &fragmentShaderSource, NULL); //字符串用\0进行结尾,不需要告诉他长度。
	int success = 0;
	char infoLog[1024];
	//4 执行Shader编译
	glCompileShader(vertex);
	//5 检查是否正确编译
	glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
	if (!success) // 0有问题,非0没有问题
	{
		glGetShaderInfoLog(vertex, 1024, NULL, infoLog); //获取日志信息
		cout << "Error Vertex Shaser Complie: " << infoLog << endl;
	}
	//4 执行fragment编译
	glCompileShader(fragment);
	//5 检查是否正确编译
	glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
	if (!success) // 0有问题,非0没有问题
	{
		glGetShaderInfoLog(vertex, 1024, NULL, infoLog); //获取日志信息
		cout << "Error Fragment Shader Complie: " << infoLog << endl;
	}

	// 创建Program壳子
	//GLuint program = 0;
	program =	glCreateProgram();
	//将编译后的结果放进Program
	glAttachShader(program, vertex);
	glAttachShader(program, fragment);

	// 执行program链接操作,形成最终的shader程序。
	glLinkProgram(program);
	//检查链接错误
	glGetProgramiv(program, GL_LINK_STATUS, &success);
	if (!success) // 0有问题,非0没有问题
	{
		glGetProgramInfoLog(program, 1024, NULL, infoLog); //获取日志信息
		cout << "Error Program Link: " << infoLog << endl;
	}
	//清理
	glDeleteShader(vertex);
	glDeleteShader(fragment);

}


void render()
{
	//GL_CALL( glClear(1) );//加上gl_call如果产生错误可以打印出来,虽然vs智能提示有问题
	GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
	//渲染操作
	//1. 使用当前的program
	glUseProgram(program);
	
	//绑定当前的vao
	glBindVertexArray(vao);
	//发出绘制指令
	//绘制三角形
	// 如果点数不够,就会连到0,0点  (123 和456)绘制两组三角形
	glDrawArrays(GL_TRIANGLES, 0, 6);
	//绘制的模式
	 // GL_TRIANGLES 绘制三角形
	 //		默认以三个点绘制一组三角形,不够三个点就不显示
	//	GL_TRIANGLE_STRIP:
	//		末尾点数为偶数【n-2 n-1 n】,基数【n-1 n-2 n】 n从0开始
	//		并且会复用之前的点
	//	GL_TRIANGLE_FAN:以扇形序列v0为起点,连接三角形。
	// GL_LINES:绘制直线
	// GL_LINE_STRIP
	//	 


}

void onResize(int width,int height)
{
	GL_CALL(glViewport(0, 0, width, height));
	cout << "onResize " << endl;
}


void frameBufferSizeCallback(GLFWwindow* win, int width, int height)
{
	std::cout << "窗体的最新大小为:" << width << "高度为:" << height << std::endl;
	//更新窗体的大小
	glViewport(0, 0, width, height);

}
 void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
 {
	 // key 字母按键码 
	 // scancode 物理按键码		 
	 // action:0抬起1按下2长按
	 // mods:是否有shift(1)或ctrl(2) 

	 cout << "key = " << key << " scancode = " << scancode << " action = " << action << " mods = " << mods << endl;

	 if (key == GLFW_KEY_W)
	 {
		 //按下了w
	 }
	 else if (action == GLFW_PRESS)
	 {
		 //按下了
	 }
	 else if (action == GLFW_RELEASE)
	 {
		 //抬起
	 }
 }

int main(int argc,char**argv)
{
	cout << "===================================" << endl;
	if (app->init(800,600) == -1)
	{
		return -1;
	}
	//设置监听帧缓冲窗口大小回调函数。
	//glfwSetFramebufferSizeCallback(win, frameBufferSizeCallback);
	//glfwSetKeyCallback(win, keyCallback);
	
	app->setResizeCallBack(onResize);
	app->setkeyCallBack(keyCallback);

	//设置OpenGL视口以及清理颜色
	glViewport(0,0,800,600);
	glClearColor(0.2f,0.3f,0.3f,1.0f);


	prepareShader();
	prepareVAOForTriangles();

	// 3. 执行窗体循环
	// 
	while (app->update() == 0 )
	{
		render();

		

	}
	// 4. 退出程序前做相关清理
	app->destroy();

	cout << "===================================" << endl;
	//const double M_PI = 3.14159265358979323846;
	//double radians = M_PI / 2; // 90度,转换为弧度
	//double sineValue = sin(radians);
	//std::cout << "sin(" << radians << ") = " << sineValue << std::endl;
	//system("pause");

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

only-lucky

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

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

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

打赏作者

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

抵扣说明:

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

余额充值