什么是shader?
一、什么是shader?
shader是一段GLSL(openGL着色语言)小程序,运行在GPU(图形处理器),而非CPU使用GLSL语言编写,看上去像c或c++,但却是另外一种不同的语言。使用shader就像写个普通程序一样,写代码-->编译-->链接在一起才能生成最终的程序。
着色器类似一个函数调用的方式--数据传输进来,经过处理,然后再传输出去。每个着色器看起来像一个完整的c程序,它的输入点就是一个名为main()的函数,但与c不同的是,GLSL的main函数没有任何参数,也没有返回值。
着色器的建立:
1>创建着色器(顶点和片元)在程序启示的位置,总是要用#version来声名所使用的版本。
glCreatShader(GLenum type),type为GL_VERTEX_SHADER或者是GL_FRAGMENT_SHADER
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
我们把需要创建的着色器类型以参数形式提供给glCreateShader。由于我们正在创建一个顶点着色器,传递的参数是GL_VERTEX_SHADER。
下一步我们把这个着色器源码附加到着色器对象上,然后编译它:void glShaderSource(GLuint shader,GLsizei count,const GLchar * const *string,const GLint *length);
顶点着色器源码(储存在一个C的字符串中),所以第二个参数为1.第三个参数是顶点着色器真正的源码。
glShaderSource(vertexShader, , &vertexShaderSource, NULL);
glCompileShader(vertexShader);
2>编译着色器
glCompileShader(Gluint shader)。结果查询使用glGetShaderiv(),例如:glGetShaderiv(Gluint shader,GL_COMPILE_STATUS,&compiled)
int success;
char infoLog[];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
首先我们定义了一个整型变量来表示是否成功编译,还定义了一个存储错误消息的容器(如果有的话)。然后用glGetShaderiv检查是否编译成功。如果编译失败,用glGetShaderInfoLog获取错误消息,然后打印出来。
if(!success)
{
glGetShaderInfoLog(vertexShader, , NULL, infoLog);
std::cout << "顶点着色器编译错误\n" << infoLog << std::endl;
}
3>把着色器添加到程序中
GLuint glCreatPogram();创建一个空的程序
void glAttachShader(GLuint program,GLuint shader);把着色器加到程序中
4>链接你的程序
链接程序的时候会把顶点着色器的输出作为片段着色器的输入。
void glLinkProgram(GLuint program);
void glGetProgramiv(program,GL_LINK_STATUS,&linked);
5>使用程序
void glUseProgram(GLuint program)
那shader到底干了什么?这取决于是哪种shader.
二、Vertex Shader
顶点着色器主要用来将点(x,y,z)变换成不同的点。顶点只是几何形状中的一个点,一个点叫vectex,多个点叫vertices。在本教程中,我们的三角形需要三个顶点(vertices)组成。
Vertex Shader的GLSL代码如下:
#version 330 core
layout(location = 0) in vec3 vert;
void main() {
// does not alter the vertices at all
gl_Position = vec4(vert, 1.0);//或者是gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
第一行#version 330告诉OpenGL这个shader使用GLSL版本3.3核心版本
第二行 layout(location = 0)设定输入变量的位置值,之后会有用到
in vec3 vert;告诉shader有一个输入变量(in)vert
第三行定义函数main,这是shader运行入口。这看上去像C,(区别:)但GLSL中main不需要带任何参数,并且不用返回,用void。
第四行gl_Position = vec4(vert, 1);将输入的顶点直接输出,变量gl_Position是OpenGL定义的全局变量,用来存储vertex shader的输出。所有vertex shaders都需要对gl_Position进行赋值。
gl_Position是4D坐标(vec4),但vert是3D坐标(vec3),所以我们需要将vert转换为4D坐标vec4(vert, 1)。第二个的参数1是赋值给第四维坐标。(因为矩阵变换均是利用齐次坐标)
Vertex Shader在本文中没有做任何事,后续我们会修改它来处理动画,摄像机和其它东西。
顶点着色器源码(储存在一个C的字符串中)
三、Fragment Shader
片元着色器的主要功能是计算每个需要绘制的像素点的颜色。
一个”fragment”基本上就是一个像素,所以你可以认为片段着色器(fragment shader)就是像素着色器(pixel shader)。在本文中每个片段都是一像素,但这并不总是这样的。你可以更改某个OpenGL设置,以便得到比像素更小的片段。
#version 330 core
out vec4 FragColor;
void main() {
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);//橘黄色
}
创建并编译:
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, , &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
现在两个着色器都编译过了,剩下的事情是把两个着色器对象链接到一个用来渲染的着色器程序中。(shader program)
四、着色器程序
着色器程序对象是多个着色器合并之后最终链接完成的,若要使用刚才编译的着色器,我们必须把它们链接为一个着色器程序对象,然后再渲染对象的时候激活这个着色器程序。
//创建
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
//依附并链接
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
得到的结果就是一个程序对象,我们可以调用glUseProgram函数,用刚创建的程序对象作为它的参数,以激活这个程序对象。
glUseProgram(shaderProgram);
就像着色器的编译一样,我们也可以检测链接着色器程序是否失败,并获取相应的日志
glGetProgramiv(shaderProgram,GL_LINK_STATUS,&success);
if (!success) {
glGetShaderInfoLog(shaderProgram, , NULL, infoLog);
cout << "着色器程序链接出错" << infoLog << endl;
}
在把着色器对象链接到程序对象以后,记得删除着色器对象,我们不再需要它们了:
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
现在,我们已经把输入顶点数据发送给了GPU,并指示了GPU如何在顶点和片段着色器中处理它。就快要完成了,但还没结束,OpenGL还不知道它该如何解释内存中的顶点数据,以及它该如何将顶点数据链接到顶点着色器的属性上。我们需要告诉OpenGL怎么做。
五、链接顶点属性
使用glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据.
glVertexAttribPointer(,,GL_FLOAT,GL_FALSE,*sizeof(float),);
glEnableVertexAttribArray();
第一个参数指定我们要配置的顶点属性.之前我们在顶点着色器中使用layout(location = 0)定义了position顶点属性的位置值(Location)。我们希望把数据传递到这一个顶点属性中,所以这里我们传入0。
第二个参数指定顶点属性的大小。顶点属性是一个vec3,它由3个值组成,所以大小是3。
第三个参数指定数据的类型,这里是GL_FLOAT(GLSL中vec*都是由浮点数值组成的)。
第四个参数定义我们是否希望数据被标准化(Normalize)。如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE。
第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。由于下个组位置数据在3个float之后,我们把步长设置为3 * sizeof(float)。要注意的是由于我们知道这个数组是紧密排列的(在两个顶点属性之间没有空隙)
最后一个参数的类型是void*,所以需要我们进行这个奇怪的强制类型转换。表示偏移量,设为0.
什么是shader?的更多相关文章
- OpenGL shader 中关于顶点坐标值的思考
今天工作中需要做一个事情: 在shader内部做一些空间距离上的计算,而且需要对所有的点进行计算,符合条件的显示,不符合条件的点不显示. 思路很简单,在vertex shader内知道顶点坐标,进行计 ...
- CSharpGL(14)用geometry shader渲染模型的法线(normal)
+BIT祝威+悄悄在此留下版了个权的信息说: CSharpGL(14)用geometry shader渲染模型的法线(normal) +BIT祝威+悄悄在此留下版了个权的信息说: 2016-08-13 ...
- 【译】Unity3D Shader 新手教程(6/6) —— 更好的卡通Shader
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 动机 如果你想了解以下几件事,我建议你阅读以下这篇教程: 想知道如何写一个multipass的toon shade ...
- 【译】Unity3D Shader 新手教程(5/6) —— Bumped Diffuse Shader
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 动机 如果你满足以下条件,我建议你阅读这篇教程: 你想学习片段着色器(Fragment Shader). 你想实现 ...
- 【译】Unity3D Shader 新手教程(4/6) —— 卡通shader(入门版)
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 暗黑系 动机 如果你满足以下条件,我建议你阅读这篇教程: 你想了解更多有关表面着色器的细节知识. 你想实现一个入门 ...
- 【译】Unity3D Shader 新手教程(3/6) —— 更加真实的积雪
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 如果你满足以下条件,我建议你阅读这篇教程: 你想知道如何在表面着色器中进行混色(blend colour) 你想实 ...
- 【译】Unity3D Shader 新手教程(2/6) —— 积雪Shader
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 如果你是一个shader编程的新手,并且你想学到下面这些酷炫的技术,我觉得你可以看看这篇教程: 实现一个积雪效果的 ...
- 【译】Unity3D Shader 新手教程(1/6)
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 刚开始接触Unity3D Shader编程时,你会发现有关shader的文档相当散,这也造成初学者对Unity3D ...
- 多材质(Shader)实现
最近在cocos creator上打算写个U3D中shader功能的插件(能在属性面板调整shader属性). 对其中一个功能有点疑惑,就是U3D中一个渲染物体上可以挂多个材质,后来查询了下,一个物体 ...
- unity的固定管线shader
最近shader学习中,看的视频. 练习的固定管线的shader如下: ps.在unity5中半透明不好用,其他的还好 //不区分大小写 //这是固定管线的Shader Shader "Sh ...
随机推荐
- C++中一些容易迷惑的语法点总结
#include<iostream> #include<cstring> using namespace std; int main(){ ][]={{,,},{,,}}; ] ...
- 大数据笔记(十四)——HBase的过滤器与Mapreduce
一. HBase过滤器 1.列值过滤器 2.列名前缀过滤器 3.多个列名前缀过滤器 4.行键过滤器5.组合过滤器 package demo; import javax.swing.RowFilter; ...
- 图解SQLSERVER联合查询和连接查询的区别
相信很多人都会用SQLSERVER联合查询和连接查询,但是用起来不一定都得心应手,对于其中的原理可能就模糊不清了,要想很牢固地掌握和运用SQL联合查询和连接查询机制,必须对其根本原理有很清晰认识, ...
- Linux安装nslookup命令
做DNS的人都知道nslookup命令是做什么用的,windows系统自带的.但是linux系统是不自带这个命令的,需要人手动安装.如果您不记得这是哪个软件包提供这个命令的话,那您还真会有些麻烦了.下 ...
- qbzt day5 下午
农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地.John打算在牧场上的某几格里种上美味的草,供他的奶牛们享 ...
- springboot定时任务出错 Unexpected use of scheduler.
java.lang.IllegalStateException: Unexpected use of scheduler. 在启动类加: @Bean public ThreadPoolTaskSche ...
- 测开之路一百零二:jquery元素操作
jquery对元素操作,获取/替换文本(.text()).html(.html()).属性(.attr()).值(.val()) html代码 text() 根据标签获取文本值 同一个标签下筛选明细 ...
- 2 Hadoop集群安装部署准备
2 Hadoop集群安装部署准备 集群安装前需要考虑的几点硬件选型--CPU.内存.磁盘.网卡等--什么配置?需要多少? 网络规划--1 GB? 10 GB?--网络拓扑? 操作系统选型及基础环境-- ...
- 【ABAP系列】SAP ABAP中使用for all entries in小结
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP中使用for a ...
- mooc-IDEA 项目/文件之间跳转--002
二.IntelliJ IDEA -项目之间跳转 1.Next Project Window :跳转到下一个项目 [ ctrl+alt+) ] 2.Previous Project Window:跳转到 ...