一、什么是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

  1. unsigned int vertexShader;
  2. vertexShader = glCreateShader(GL_VERTEX_SHADER);

我们把需要创建的着色器类型以参数形式提供给glCreateShader。由于我们正在创建一个顶点着色器,传递的参数是GL_VERTEX_SHADER

下一步我们把这个着色器源码附加到着色器对象上,然后编译它:void  glShaderSource(GLuint shader,GLsizei count,const GLchar * const *string,const GLint *length);

顶点着色器源码(储存在一个C的字符串中),所以第二个参数为1.第三个参数是顶点着色器真正的源码。

  1. glShaderSource(vertexShader, , &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

2>编译着色器

glCompileShader(Gluint shader)。结果查询使用glGetShaderiv(),例如:glGetShaderiv(Gluint shader,GL_COMPILE_STATUS,&compiled)

  1. int success;
  2. char infoLog[];
  3. glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);

首先我们定义了一个整型变量来表示是否成功编译,还定义了一个存储错误消息的容器(如果有的话)。然后用glGetShaderiv检查是否编译成功。如果编译失败,用glGetShaderInfoLog获取错误消息,然后打印出来。

  1. if(!success)
  2. {
  3. glGetShaderInfoLog(vertexShader, , NULL, infoLog);
  4. std::cout << "顶点着色器编译错误\n" << infoLog << std::endl;
  5. }

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代码如下:

  1. #version 330 core
  2. layout(location = 0) in vec3 vert;
  3. void main() {
  4. // does not alter the vertices at all
  5. gl_Position = vec4(vert, 1.0);//或者是gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
  6. }

第一行#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设置,以便得到比像素更小的片段。

  1. #version 330 core
    out vec4 FragColor;
    void main() {
      FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);//橘黄色
    }

创建并编译:

  1. unsigned int fragmentShader;
  2. fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  3. glShaderSource(fragmentShader, , &fragmentShaderSource, NULL);
  4. glCompileShader(fragmentShader);

现在两个着色器都编译过了,剩下的事情是把两个着色器对象链接到一个用来渲染的着色器程序中。(shader program)

四、着色器程序

着色器程序对象是多个着色器合并之后最终链接完成的,若要使用刚才编译的着色器,我们必须把它们链接为一个着色器程序对象,然后再渲染对象的时候激活这个着色器程序。

  1. //创建
  2. unsigned int shaderProgram;
  3. shaderProgram = glCreateProgram();
  4. //依附并链接
  5. glAttachShader(shaderProgram, vertexShader);
  6. glAttachShader(shaderProgram, fragmentShader);
  7. glLinkProgram(shaderProgram);

得到的结果就是一个程序对象,我们可以调用glUseProgram函数,用刚创建的程序对象作为它的参数,以激活这个程序对象。

  1. glUseProgram(shaderProgram);

就像着色器的编译一样,我们也可以检测链接着色器程序是否失败,并获取相应的日志

  1. glGetProgramiv(shaderProgram,GL_LINK_STATUS,&success);
  2. if (!success) {
  3. glGetShaderInfoLog(shaderProgram, , NULL, infoLog);
  4. cout << "着色器程序链接出错" << infoLog << endl;
  5. }

在把着色器对象链接到程序对象以后,记得删除着色器对象,我们不再需要它们了:

  1. glDeleteShader(vertexShader);
  2. glDeleteShader(fragmentShader);

现在,我们已经把输入顶点数据发送给了GPU,并指示了GPU如何在顶点和片段着色器中处理它。就快要完成了,但还没结束,OpenGL还不知道它该如何解释内存中的顶点数据,以及它该如何将顶点数据链接到顶点着色器的属性上。我们需要告诉OpenGL怎么做。

五、链接顶点属性

使用glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据.

  1. glVertexAttribPointer(,,GL_FLOAT,GL_FALSE,*sizeof(float),);
  2. 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. (转)linux nc命令使用详解

    linux nc命令使用详解 原文:https://www.2cto.com/os/201306/220971.html 功能说明:功能强大的网络工具 语 法:nc [-hlnruz][-g<网 ...

  2. VMware 虚拟化编程(14) — VDDK 的高级传输模式详解

    目录 目录 前文列表 虚拟磁盘数据的传输方式 Transport Methods Local File Access NBD and NBDSSL Transport SAN Transport Ho ...

  3. 记2018最后一次问题诊断-Spark on Yarn所有任务运行失败

    2018的最后一个工作日,是在调式和诊断问题的过程中度过,原本可以按时下班,毕竟最后一天了,然鹅,确是一直苦苦挣扎. 废话不多说,先描述一下问题:有一套大数据环境,是CDH版本的,总共4台机子,我们的 ...

  4. struts2 基础3 文件上传、拦截器

    文件上传: 1.将头设置为enctype=”multipart/form-data” <form action="${pageContext.request.contextPath } ...

  5. 【Unity Shader】---入门知识点

    着色器声明(“名字”)Shader "ShaderDiffuseExample" { 一.属性定义(作用:外部传入参数) 属性定义语法:PropName("Display ...

  6. 流程控制: if分支 while循环 for循环

    流程控制 Python程序执行,一定按照某种规律在执行 1.宏观一定是自上而下(逻辑上方代码一定比逻辑下方代码先执行):顺序结构 2.遇到需要条件判断选择不同执行路线的执行方式:分支结构 3.有些事情 ...

  7. Echars -- 在Vue中如何使用Echars

    在vue-cli项目中使用echarts -->Wangqi   这个示例使用 vue-cli 脚手架搭建 安装echarts依赖 npm install echarts -S 或者使用国内的淘 ...

  8. spring boot 将对象转换为json返回

    Spring Boot默认使用Jackson将对象转换为json,需要配置以下依赖: compile group: 'com.fasterxml.jackson.core', name: 'jacks ...

  9. JAVA总结--多线程

    一.概念 1.进程:一个具有一定独立功能的程序,关于某些数据集合,一次运行活动. 两点:1.有自己的空间存储数据:2.一个程序. 进程,是系统   进行   资源分配  和 调度  的基础单位.动态性 ...

  10. HDU-2068 RPG的错排(组合, 错排)

    RPG的错排 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Sub ...