openGL学习之路

      shader学习貌似进入了一个瓶颈,可以看懂,但是真正上手的时候大脑却一片混乱,所以就想着从更加基本的地方进入(好吧,还是数学问题),看看能不能增加一些奇怪的经验值。。。

以下使用windows + vs2017 + glfw + glew + glm

 https://www.bilibili.com/video/av24353839/?p=23

https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/#_2

=========================================Draw===========================================

       openGL里面的draw一般指的是glDrawArray之类的渲染指令,可是,他是怎么绑定上去的,我每帧都需要传送数据吗?

!!!!!!以下我不确定是否为事实,只是自己的一些看法

先看一段程序块

---------vertex.shader----------
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec4 ourColor;
out vec2 TexCoord;

uniform mat4 modleSpace;
uniform mat4 viewSpace;
uniform mat4 perjSpace;

void main()
{
        // gl_Position是openGL顶点着色器自带的必须返回的顶点数据
        // 而这段是计算“我”这个物体应该出现在窗口的位置和角度
        gl_Position = perjSpace * viewSpace * modleSpace * vec4(aPos, 1.0);
	ourColor = vec4(1.0,0,0,1.0);
	TexCoord = aTexCoord;
}
---------main.cpp--------
......
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);

unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 首先绑定

// vertices 顶点,https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/#3d_1 太长了,就不复制了
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW); // 将数据复制到缓存

unsigned int EBO; // element buffer object
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

// 创建特征码,并填充数据到相应特征码中
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);

unsigned int COL;
glGenBuffers(1, &COL);
glBindBuffer(GL_ARRAY_BUFFER, COL);
glBufferData(GL_ARRAY_BUFFER, sizeof(color), &color, GL_STATIC_DRAW);

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);

..........
while (!glfwWindowShouldClose(window))
{
	//matrix = glm::rotate(matrix, glm::radians(1.0f), glm::vec3(0.0f, 0.0f, 1.0));

	glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glm::mat4 modleSpace;
	glm::mat4 viewSpace;
	glm::mat4 perjSpace;

	modleSpace = glm::translate(modleSpace, glm::vec3(0, 1, 0));

	modleSpace = glm::rotate(modleSpace, glm::radians(10.0f) + (float)glfwGetTime(), glm::vec3(1.0, 0, 0));

	//viewSpace = glm::translate(viewSpace, glm::vec3(-_right, 0, _forward));
	viewSpace = glm::translate(viewSpace, glm::vec3(0, 0, -3));
        // 透视投影矩阵,也就是camera里面的fov,near之类的
	perjSpace = glm::perspective(glm::radians(45.0f), (float)800 / (float)600, 0.1f, 10.0f);   
	perjSpace = glm::translate(perjSpace, glm::vec3(-_right, 0, _forward));
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, texture1);
	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, texture2);

        // 传递给shader参数,矩阵
	glUniformMatrix4fv(glGetUniformLocation(shader.Shader_Program_ID, "modleSpace"), 1, GL_FALSE, glm::value_ptr(modleSpace));

	glUniformMatrix4fv(glGetUniformLocation(shader.Shader_Program_ID, "viewSpace"), 1, GL_FALSE, glm::value_ptr(viewSpace)); 

	glUniformMatrix4fv(glGetUniformLocation(shader.Shader_Program_ID, "perjSpace"), 1, GL_FALSE, glm::value_ptr(perjSpace));

	shader.Use();

	glDrawArrays(GL_TRIANGLES, 0, 36);
	
	glfwSwapBuffers(window);
	glfwPollEvents();
	
}
......

// 模型矩阵,用以计算物体在世界空间中的位置
// 物体出现在 worldUP * 1 的坐标电商
modleSpace = glm::translate(modleSpace, glm::vec3(0, 1, 0));

// 观察矩阵,也就是Camera会出现在那个位置,这里Camera会出现在 -3 * worldForward 的坐标上
viewSpace = glm::translate(viewSpace, glm::vec3(0, 0, -3));

// 透视投影矩阵,可以理解为是一个相机的大部分参数
// 第一个 -> 这个相机的角度有多大             对应unity fov
// 第二个 -> 这个相机最大视角的高宽比       
// 第三个 -> 这个相机的开口距离相机本体的距离  对应unity near
// 第四个 -> 这个相机可以看到多远的位置        对应unity far
perjSpace = glm::perspective(glm::radians(45.0f), (float)800 / (float)600, 0.1f, 10.0f); 

可以看到,顶点的复制和读入只有一次,也就是glBufferData(),之后的操作我都没有再次操作顶点数据,而是通过变换矩阵进行的视角移动,这样的话,我的draw有几个?

       众所周知,GPU的计算图元是远远大于他的内存单元的,这也就导致了GPU的计算速度很快,但是存储之类的却是异想天开,但是我们一个场景里面可是会有很多物体的啊,这些物体的的数据应该怎么去处理,

        (PS:如果你能看到这个PS,就说明这段话我不确定)猜想一下,glBufferData其实并不是将数据存入了GPU,而是保存了一个相应数据的地址,&并把他压入对应的数据管道缓冲中(VBO,vertex buffer object),当我调用Draw等绘图指令的时候,他会依照对应管道的相应索引来将其一一放入一个列表中,GPU此时会按照相应管道中的顺序依次绘制,完成一帧,那么也就是说,当我们调用glBufferData的时候,CPU就会把一些数据暂存到与GPU通信的管道中,而当CPU发送一次Draw指令的时候,GPU会从这些管道中将数据的副本保存下来,然后处理,丢弃,在处理下一个,直到索引到达终点。

         用unity里面的说法来说就是,当一帧开始的时候,GPU会先从CPU那里拿走相应数据的索引,然后按照索引逐步的进行绘制,直到索引超出,完成一帧的绘制,也就是说其实Draw只是发送一个指令,CPU告诉GPU,这一帧开始了,你处理一下我放在XX地方的数据,此时,GPU开始处理自己的工作,把CPU存入的数据依次拿出来,根据数据的相应指示绘制到屏幕上(也就是上面代码里面的layout,告诉GPU,那些数据应该放在哪里),那么shader呢,我们好像没有看到shader什么事啊,其实shader就是GPU的接收函数,比如上面的shader,他会接收顶点,颜色,uv坐标等,传入之后GPU再次处理下一个,shader进行自己的工作,以上,一帧完成。

==========================================变换=============================================

        变换的相应知识,可以在 https://learnopengl-cn.github.io/01%20Getting%20started/07%20Transformations/

        我们在写shader(unity)的时候,经常会看到mul,他会接受一个matrix4*4,vec4 的参数(当然,也可以matrix3*3,vec3,因为要满足行列乘法嘛~),那么这个到底有什么作用,

        在这里,就要说一下坐标系的问题了,世界坐标,以标准的0.0作为世界中心,所有的物体都会在这个坐标系上有相应的位置,而局部坐标系,则是以自身为基准(0.0),自身的正前方为z轴,假想一下,如果我们所有的物体都不进行转换,直接使用自身的local坐标来进行操作,会发生什么?一堆物体在(x,y)坐标上,你遮住我,我遮住你,所有物体都说我的坐标是对的,根据shader的处理,我就应该显示我的背面,脸我不要了,那么会发生什么~~~

         camera躲在世界坐标看着前面一部分显示,一部分不显示,一部分显示了一部分,另一些我乍一看看不见,然后稍微走两步他就突然出现了,此时camera的心情应该是这样的????????????????????????????????????,我是谁,我在那,我要干嘛??????

          所以,物体最终显示在屏幕中其实都是需要经过几次变换的

https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/ 

           第一步,也就是从本地坐标先转化为世界坐标(也就是自身的坐标位置*自身的模型矩阵,上面代码中的modleSpace = glm::translate...),此时,我们得到了物体在世界空间中的相对坐标

            第二步,也就是从世界的坐标转化为相对于相机的坐标位置(自身的坐标+camera坐标),此时camera就可以知道我们的物体到底在自己的那个位置

           第三步(伪):(正射投影,看名字也能了解一二,就是正交相机)

            第三步,也是最复杂的一步,将所有的数据按照透视投影矩阵进行归一化,所有超出的数据都将变为不可见,关于这个算法实现(上面代码中的 perjSpace = glm::perspective.....)https://www.bilibili.com/video/av24353839/?p=23

其实。放到unity里面大概就是这样的

// unity里面会自己帮助你进行几次空间变换,所以一般我们写的shader很少会用到多次变换,但是这里只是过程

// 注意,unity和openGL左右手坐标系不同,所以矩阵相乘的顺序也不一样


----------------------------------------------------------------------------------------
// 以下参数,aPos 为顶点,aColor为顶点颜色,aTexCoord为uv坐标,gl_Position为我们需要返回的顶点位置,在unity里面我们不需要写

//gl_Position其实不应该写到a2v里面,因为在openGL里面这个并不是传递给frag的参数
//而是直接回传给GPU进行直接渲染的参数,而如果想要在frag里面修改顶点数据,则应该在声明一个参数,
//传递给frag

struct a2v
{
    fixed3 aColor      : COLOR;
    fixed3 aPos        : POSITION;
    fixed3 aTexcoord   : TEXCOORD0;
    fixed4 gl_Position : POSITION; // 注意这个是openGL需要返回出去的位置
};

struct v2f
{
    fixed4 ourColor : COLOR;
    fixed3 TexCoord : TEXCOORD0;
};

// openGL里面的顶点着色器,unity里面是#pragma vertex vert
v2f vert(a2v v) 
{
    // 变换坐标到世界空间,modleSpace是模型矩阵
    // gl_Position = modleSpace * vec4(aPos, 1.0)
    gl_Postion = mul((float4*4)unity_ObjectToWorld,fixed4(aPos,1.0));
    
    // 把物体转换到相机坐标系
    // gl_Position = glPosition + _WorldSpaceCameraPos.xyz;

    // 把物体变换到视图,坐标系
    // gl_Position = viewSpace * gl_Position;
    gl_Position = mul((float4*4)UNITY_MATRIX_V,gl_Position);

    // 把坐标变换到裁剪空间,也就是透视投影矩阵
    // gl_Position = perjSpace * gl_Position;
    gl_Position = mul((float4*4)UNITY_MATRIX_VP,gl_Position);

}

上面步骤完成之后,我们其实就已经获得了一个物体应该出现的相对于相机的位置,旋转,他应该出现多少在相机的视角之中,

其实,我们在unity中也能看到一些矩阵信息,用过Profiler的都知道,在绘制帧的时候,下方最常出现的就是UNITY_MATRIX_VP

也就是上面的第三步,将物体从世界空间转化到裁剪空间中,

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

猜你喜欢

转载自blog.csdn.net/MikuLingSSS/article/details/85010304
今日推荐