一、什么是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. 箭头函数(Arrow Functions)

    ES5语法: var getPrice = function() { return 4.55; }; console.log(getPrice()); ES6 中,箭头函数就是函数的一种简写形式,使用 ...

  2. 1>/dev/null 2>&1的含义

      shell中可能经常能看到:>/dev/null 2>&1    分解这个组合:“>/dev/null 2>&1” 为五部分. 1:> 代表重定向到哪 ...

  3. hive_action

    w pdf469 [不直接MR访问数据的工具   查询间接转化为MR] https://en.wikipedia.org/wiki/Apache_Hive Apache Hive supports a ...

  4. Flask中的实例化配置

    也就是在app=Flask(__name__)括号中的参数 1.static_folder = 'static', # 静态文件目录的路径 默认当前项目中的static目录 2.static_url_ ...

  5. Delphi XE2 之 FireMonkey 入门(19) - TFmxObject 的子类们(表)

    参考: 和 FMX 相关的类(表) TFmxObject IFreeNotification             TAnimation TBitmapAnimation           TBi ...

  6. javaScript 递归 闭包 私有变量

    递归 递归的概念 在程序中函数直接或者间接调用自己. 跳出结构,有了跳出才有结果. 递归的思想 递归的调用,最终还是要转换为自己这个函数.    应用 function sum(n){ if(n == ...

  7. node+express 发送get请求

    var express = require('express') , app = express(); var querystring = require('querystring'); var ut ...

  8. BZOJ[3728]PA2014 Final Zarowki

    有n个房间和n盏灯,你需要在每个房间里放入一盏灯.每盏灯都有一定功率,每间房间都需要不少于一定功率的灯泡才可以完全照亮. 你可以去附近的商店换新灯泡,商店里所有正整数功率的灯泡都有售.但由于背包空间有 ...

  9. Machine Learning 文章导读

    Machine Learning Algorithms Linear Regression and Gradient Descent Local Weighted Regression Algorit ...

  10. kNN算法实例(约会对象喜好预测和手写识别)

    import numpy as np import operator import random import os def file2matrix(filePath):#从文本中提取特征矩阵和标签 ...