一、什么是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?的更多相关文章

  1. OpenGL shader 中关于顶点坐标值的思考

    今天工作中需要做一个事情: 在shader内部做一些空间距离上的计算,而且需要对所有的点进行计算,符合条件的显示,不符合条件的点不显示. 思路很简单,在vertex shader内知道顶点坐标,进行计 ...

  2. CSharpGL(14)用geometry shader渲染模型的法线(normal)

    +BIT祝威+悄悄在此留下版了个权的信息说: CSharpGL(14)用geometry shader渲染模型的法线(normal) +BIT祝威+悄悄在此留下版了个权的信息说: 2016-08-13 ...

  3. 【译】Unity3D Shader 新手教程(6/6) —— 更好的卡通Shader

    本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 动机 如果你想了解以下几件事,我建议你阅读以下这篇教程: 想知道如何写一个multipass的toon shade ...

  4. 【译】Unity3D Shader 新手教程(5/6) —— Bumped Diffuse Shader

    本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 动机 如果你满足以下条件,我建议你阅读这篇教程: 你想学习片段着色器(Fragment Shader). 你想实现 ...

  5. 【译】Unity3D Shader 新手教程(4/6) —— 卡通shader(入门版)

    本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 暗黑系 动机 如果你满足以下条件,我建议你阅读这篇教程: 你想了解更多有关表面着色器的细节知识. 你想实现一个入门 ...

  6. 【译】Unity3D Shader 新手教程(3/6) —— 更加真实的积雪

    本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 如果你满足以下条件,我建议你阅读这篇教程: 你想知道如何在表面着色器中进行混色(blend colour) 你想实 ...

  7. 【译】Unity3D Shader 新手教程(2/6) —— 积雪Shader

    本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 如果你是一个shader编程的新手,并且你想学到下面这些酷炫的技术,我觉得你可以看看这篇教程: 实现一个积雪效果的 ...

  8. 【译】Unity3D Shader 新手教程(1/6)

    本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 刚开始接触Unity3D Shader编程时,你会发现有关shader的文档相当散,这也造成初学者对Unity3D ...

  9. 多材质(Shader)实现

    最近在cocos creator上打算写个U3D中shader功能的插件(能在属性面板调整shader属性). 对其中一个功能有点疑惑,就是U3D中一个渲染物体上可以挂多个材质,后来查询了下,一个物体 ...

  10. unity的固定管线shader

    最近shader学习中,看的视频. 练习的固定管线的shader如下: ps.在unity5中半透明不好用,其他的还好 //不区分大小写 //这是固定管线的Shader Shader "Sh ...

随机推荐

  1. Elasticsearch的聚合操作

    ES的聚合: Metrics 简单的对过滤出来的数据集进行avg,max等操作,是一个单一的数值. bucket 可以理解为将过滤出来的数据集按条件分成多个小数据集,然后Metrics会分别作用在这些 ...

  2. 重新理解了重定向,利用重定向可以防止用户重复提交表单(兼谈springmvc重定向操作)

    自己用springmvc框架有一段时间了,但是都还一直分不清楚什么时候应该用转发,什么时候应该用重定向.可能用转发的情形太多了,以致于自己都忘记了还有重定向. 当用户提交post请求之后,刷新页面就会 ...

  3. DAY 5 下午

    每个点一定属于一个重链 重链条数和轻边边数是logn级别 证明和启发式合并差不多 因为轻子树的大小至少是重子树大小-1 树链剖分:两遍dfs 第一次:统计子树大小,确定重儿子 第二次:把重链剖出来 每 ...

  4. ASP 解析json

    第一个方法是使用 JScript : <script language="jscript" runat="server"> Array.protot ...

  5. 三十六、python 中subprocess介绍

    import subprocess 1.执行系统命令subprocess.call('ipconfig') #shell=False时,拼接命令分开写,放在列表中,等于True时,可写一块,空格隔开例 ...

  6. 测开之路九十四:css之盒子模型

    盒子模型 为了演示方便,把内容放到盒子里面 引用css 演示内容 外边距: 4个方向分开写 简写为一条指令,顺序为上右下左 简写为一条指令,第一个值为上下,第二个值为左右 简写为一条指令,只有一个值时 ...

  7. winform控件CxFlatUI

    CxFlatUI https://github.com/HuJinguang/CxFlatUI    当前控件 AlertBox Button CheckBox DatePicker GroupBox ...

  8. day16模块,导入模板完成的三件事,起别名,模块的分类,模块的加载顺序,环境变量,from...import语法导入,from...import *,链式导入,循环导入

    复习 ''' 1.生成器中的send方法 -- 给当前停止的yield发生信息 -- 内部调用__next__()取到下一个yield的返回值 2.递归:函数的(直接,间接)自调用 -- 回溯 与 递 ...

  9. linux下升级php5.4到php5.6

    进入终端后查看php版本 php -v 输出可能如下: PHP 5.4.35 (cli) (built: Nov 14 2014 07:04:10) Copyright (c) 1997-2014 T ...

  10. 007/Docker(一)

    Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口. Docker 将应用程序与该程序的依赖,打包在一个文件里面.运行这个文件,就会生成一个虚拟容器.程序在这个虚拟容器里运行, ...