初级着色器

3D坐标转2D坐标,由OpenGL图形渲染管线管理。
Graphics Pipeline:管线:实际上指的是一堆原始图形数据途经一个输送管道,期间经过各种变化处理最终出现在屏幕的过程




2D坐标与像素不同:
2D坐标精确表示一个点在2D空间中的位置,而2D像素是这个点的近似值,2D像素受到你的屏幕/窗口分辨率的限制。




OpenGl着色器:OpenGL Shading Language, GLSL


图元(Primitive):OpenGL需要你去指定这些数据所表示的渲染类型,例子:GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP。




图形渲染管线的几个阶段?
一、
顶点着色器(Vertex Shader):顶点着色器主要的目的是把3D坐标转为另一种3D坐标(后面会解释),同时顶点着色器允许我们对顶点属性进行一些基本处理。
二、
图元装配(Primitive Assembly)阶段:把着色器的顶点作为输入。装配成指定的图元形状。
三、
几何着色器(Geometry Shader):把图元形式的顶点集作为输入,并通过构造新顶点产生形状。本例是三角形。
四、
光栅化阶段(Rasterization Stage):把图元映射为屏幕上的像素,生成(Fragment)片段(供片段着色器使用Fragment Shader)
五、
片段之前会裁切(Clipping),裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。


***OpenGL中的一个片段是OpenGL渲染一个像素所需的所有数据。***
六、
片段着色器的主要目的是计算一个像素的最终颜色。
七、
Alpha测试和混合(Blending)阶段,这个阶段检测片段的对应的深度(和模板(Stencil))值(后面会讲),用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。




标准化设备坐标(Normalized Device Coordinates, NDC)


:标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间


我们通过顶点缓冲对象(Vertex Buffer Objects, VBO)管理这个内存:好处:对象比点发送的速度要快。


绑定缓冲:OpenGL允许同时绑定多个缓冲,只要是不同类型的缓冲,使用glBindBuffer函数吧新创建的缓冲绑定到GL_ARRAY_BUFFER上。


GLuint VBO;
glGenBuffers(1, &VBO);  
生成VBO对象。
OpenGl有很多 缓冲对象类型,  但对于《《顶点缓冲对象》》的类型是GL_ARRAY_BUFFER.
然后呢?
glBindBuffer(GL_ARRAY_BUFFER, VBO);  
这是吧VBO绑定到GL_ARRAY_BUFFER目标上,
所以,之后的任何缓冲调用都会用来配置当前绑定的缓冲(VBO)。


glBufferDate(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
把之前定义的顶点数据复制到缓冲内存中。glBufferDate是专门把用户定义的数据复制到当前绑定缓冲的函数。
四个参数:1,目标缓冲的类型,绑定到谁身上。
2,传输数据的大小(字节) 3,发送的数据 4,显卡管理形式:
(1)  GL_STATIC_DRAW :数据不会或几乎不会改变。
(2)  GL_DYNAMIC_DRAW:数据会被改变很多。
(3)  GL_STREAM_DRAW :数据每次绘制时都会改变。






片段着色器:控制颜色输出:RGBA:红色、绿色、蓝色和alpha(透明度)分量
OpenGl和GLSL定义的颜色,分量强度设置在0.0到1.0之间。
(Fragment Shader:


下步骤:
将数据与着色器属性链接。


索引缓冲对象(Element Buffer Object,EBO,也叫Index Buffer Object,IBO):
作用:专门储存索引


创建索引缓冲对象:
GLuint EBO;
glGenBuffers(1, &EBO);
然后是绑定:绑定的缓冲类型定义为:GL_ELEMENT_ARRAY_BUFFER
之后把索引复制到缓冲里。
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 


最后一件要做的事是用glDrawElements来替换glDrawArrays函数,来指明我们从索引缓冲渲染
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);:

四个参数:绘制模式、顶点数、索引类型、偏移量。



下面是基础图形代码:

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
#include <iostream>

void key_callback(GLFWwindow* windows, int key, int scancode, int action, int mode);

void key_callback2(GLFWwindow* windows, int key, int scancode, int action, int mode);

//顶点着色器源码:
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout(location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"	gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";

const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\0";
int main()
{

	std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl;

	//这都是初始化GLFW,后面的要看文档
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);


	//创建窗口,
	GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);//通知GLFW将我们窗口的上下文设置为当前线程的主上下文


	//不知道回调函数为什么要加在这里??
	//glfwSetKeyCallback(window, key_callback2);回调函数只有最下面那个有作用
	glfwSetKeyCallback(window, key_callback);


	//初始化GLEW(管理OpenGL的函数指针)
	glewExperimental = GL_TRUE;
	if (glewInit() != GLEW_OK)
	{
		std::cout << "Failed to initialize GLEW" << std::endl;
		return -1;
	}

	//视口,OpenGl知道相对于窗口大小显示数据和坐标
	int width, height;
	glfwGetFramebufferSize(window, &width, &height);
	glViewport(0, 0, width, height);//前两个控制左下角的位置,后面是窗口宽度高度(像素)

	//动态编译着色器源码:
	GLuint vertexShader;
	//把创建着色器类型以参数的形式提供给glCreateShader,定点着色器,传递的参数是GL_VERTEX_SHADER.
	vertexShader = glCreateShader(GL_VERTEX_SHADER);
	//下一步把着色器的源码附加到着色器对象上,然后编译它
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
	glCompileShader(vertexShader);


	//下面代码是检查是否编译成功
	GLint success;
	GLchar infoLog[512];
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
	//用glGetShaderiv检查是否便宜成功,如果失败,获取错误消息,然后打印
	if (!success)
	{
		glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
	}

	//片段着色器同样的道理
	GLuint fragmentShader;
	fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
	glCompileShader(fragmentShader);
	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
	}

	//下面是把多个着色器链接为一个着色器程序对象
	GLuint shaderProgram;
	shaderProgram = glCreateProgram();
	//glCreateProgram函数创建一个程序,并返回新创建程序对象的ID引用。
	//后面是附加,把之前的着色器附加到程序对象上,然后glLinkProgram链接他们
	glAttachShader(shaderProgram, vertexShader);
	glAttachShader(shaderProgram, fragmentShader);
	glLinkProgram(shaderProgram);
	//检查编译错误
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
	}
	//链接完厚删除着色器对象
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);


	GLfloat vertices[] = {
		0.5f, 0.5f, 0.0f,   // 右上角
		0.5f, -0.5f, 0.0f,  // 右下角
		-0.5f, -0.5f, 0.0f, // 左下角
		-0.5f, 0.5f, 0.0f,   // 左上角
		
	};

	GLuint indices[] = {
		0,1,3,
		1,2,3,
		0,2,3,
	
	};

	GLuint VBO, VAO,EBO;
	//使用glGenBuffers函数和一个缓冲ID生成一个VBO对象??
	glGenBuffers(1, &VBO);
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &EBO);

	//绑定VAO
	glBindVertexArray(VAO);
	//复制顶点数组到缓冲中,glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
	glBindBuffer(GL_ARRAY_BUFFER, VBO);//glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
	//设置定点属性指针

	//在这里加上索引缓冲,复制我们的索引数组到一个索引缓冲中,供OpenGL使用
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);


	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
	glEnableVertexAttribArray(0);
	//安全?
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	//解绑VAO
	glBindVertexArray(0);

	//防止退出的循环
	while (!glfwWindowShouldClose(window))
	{
		glfwPollEvents();


		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		//glClearColor是一个状态设置函数,glClear是一个状态应用函数

		//绘图
		glUseProgram(shaderProgram);
		glBindVertexArray(VAO);
		//glDrawArrays(GL_TRIANGLES, 0, 3);//绘制图形函数,0-3是数组索引

		//用glDrawElements来替换glDrawArrays函数,来指明我们从索引缓冲渲染
		//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

		//线框模式
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		glDrawElements(GL_TRIANGLES, 9, GL_UNSIGNED_INT, 0);
		glBindVertexArray(0);
		

		glfwSwapBuffers(window);
	}

	//回调函数
	
	//释放:
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);

	glfwTerminate(); //释放/删除之前的分配的所有资源
	return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{

	std::cout << key << std::endl;
	//用户按下ESC键,我们设置window窗口WindowShouldClose属性为true
	//关闭应用程序
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
		glfwSetWindowShouldClose(window, GL_TRUE);
}
void key_callback2(GLFWwindow* window, int key, int scancode, int action, int mode)
{

	std::cout << key << std::endl;
	//用户按下ESC键,我们设置window窗口WindowShouldClose属性为true
	//关闭应用程序
	if (key == 65)
		std::cout << "成功回调A键" << std::endl;
}
//问题:对ABO和VAO的并不怎么理解?他们的区别在这个例子中也看不出来。





简单的几种图形和线框模式很容易做出来了。


下面是几个练习:

1.Try to draw 2 triangles next to each other using glDrawArrays by adding more vertices to your data:

增加定点后,添加一个绘制三角形函数即可。glDrawArrays:

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

glDrawArrays(GL_TRIANGLES, 0, 3);//绘制图形函数,0-3是数组索引
glDrawArrays(GL_TRIANGLES, 3, 6);


或者也可以增加到6个点,之后:glDrawArrays(GL_TRIANGLES, 0, 6);


2.Now create the same 2 triangles using two different VAOs and VBOs for their data:

该练习告诉你的是VBO,VAO也可以是数组,可以多个。


建立两个顶点数组:

GLfloat firstTriangle[] = {
		-0.9f,-0.5f,0.0f,
		-0.0f,-0.5f,0.0f,
		-0.45f,0.5f,0.0f
	};
	GLfloat secondTriangle[] = {
		0.9f,-0.5f,0.0f,
		0.0f,-0.5f,0.0f,
		0.45f,0.5f,0.0f
	};

绑定2次:

GLuint VBOs[2], VAOs[2],EBO;
	//使用glGenBuffers函数和一个缓冲ID生成一个VBO对象??
	glGenBuffers(2, VBOs);
	glGenVertexArrays(2, VAOs);
	glGenBuffers(1, &EBO);

	//绑定VAO
	glBindVertexArray(VAOs[0]);
	//复制顶点数组到缓冲中,glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
	glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);//glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上
	glBufferData(GL_ARRAY_BUFFER, sizeof(firstTriangle), firstTriangle, GL_STATIC_DRAW);


	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

	//设置顶点属性指针
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
	glEnableVertexAttribArray(0);
	glBindVertexArray(0);
	//安全?
	glBindBuffer(GL_ARRAY_BUFFER, 0);

绘图两次即可:

		glUseProgram(shaderProgram);
		glBindVertexArray(VAOs[0]);
		glDrawArrays(GL_TRIANGLES, 0, 3);//绘制图形函数,0-3是数组索引
		
		glBindVertexArray(VAOs[1]);
		glDrawArrays(GL_TRIANGLES, 0, 3);

		glBindVertexArray(0);


3.Create two shader programs where the second program uses a different fragment shader that outputs the color yellow; draw both triangles again where one outputs the color yellow:

我自己是写的数组,写两个片段着色器两套源码。也可以写两个名字两个片段着色器。

	GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
	GLuint fragmentShaderOrange = glCreateShader(GL_FRAGMENT_SHADER);
	GLuint fragmentShaderYellow = glCreateShader(GL_FRAGMENT_SHADER);
	GLuint shaderProgramOrange = glCreateProgram();
	GLuint shaderProgramYellow = glCreateProgram();
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
	glCompileShader(vertexShader);
	glShaderSource(fragmentShaderOrange, 1, &fragmentShaderSource, NULL);
	glCompileShader(fragmentShaderOrange);
	glShaderSource(fragmentShaderYellow, 1, &fragmentShaderSource2, NULL);
	glCompileShader(fragmentShaderYellow);
	glAttachShader(shaderProgramOrange, vertexShader);
	glAttachShader(shaderProgramOrange, fragmentShaderOrange);
	glLinkProgram(shaderProgramOrange);
	glAttachShader(shaderProgramYellow, vertexShader);
	glAttachShader(shaderProgramYellow, fragmentShaderYellow);
	glLinkProgram(shaderProgramYellow);



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值