精华内容
下载资源
问答
  • 一、OpenGL ES ...OpenGL ES顶点着色器、片元着色器等对图形的处理运行在GPU上,是服务端。客户端向服务端提供数据,服务端通过顶点着色器、片元着色器等计算数据并显示。 二、 OpenGL ES 3.0 图

    一、OpenGL ES

    OpenGL ES是使用在手机端和嵌入式里的3D图形应用程序编程接口,是跨平台的API。OpenGL ES是OpenGL的简化版本。

    OpenGL2.x 版本相比 1.x 版本有较大差异,1.x 版本为 fixed function pipeline,即固定管线硬件,而 2.x 版本为 programmable pipeline,可编程管线硬件。

    固定管线中原本由系统做的一部分工作,在可编程管线中必须需要自己写程序实现,具体程序为 vertex shader(顶点着色器)和 fragment shader(片元着色器)。

    二、 OpenGL ES 3.0 图形管线

    • 使用API创建顶点数据(纹理坐标也是顶点数据)存入顶点缓冲区/数组对象,顶点缓冲区是GPU显存上的区间,顶点数组存储在内存中,数据会传给顶点着色器;
    • 顶点着色器对glPiont赋值、执行旋转缩放变换等操作,之后进行图元装配;
    • 图元装配中设置顶点之间的连接方式来确定绘制方式,然后绘制图形,之后对图形进行光栅化;
    • 光栅化是确定图形在屏幕上的像素点,便于后续片元着色器填充颜色;
    • 纹理坐标数据是不断变化的,需要通过attribute属性通道交给顶点着色器往下传递到片元着色器;
    • 片元着色器对各个像素点填充颜色,可以对图形填充纯色,也可以从纹理数据中获取对应位置的颜色填充;
    • 逐片元操作是将图形中的每一个像素点填充颜色,把结果放在帧缓冲区中;
    • 从帧缓冲区将结果显示到屏幕上。

    tips:图元装配、光栅化、逐片元操作和帧缓冲区是不由开发者参与的,OpenGL会自动计算。

    三、OpenGL ES图形管道

    1. application应用:程序代码将图片原始数据如顶点数据和图片数据传递给顶点着色器;
    2. Vertex顶点着色器:顶点着色器处理旋转缩放等变换数据,需要光照的添加光照数据,将这些数据往下传递进行图元装配;
    3. Geometry几何处理:对图形的处理,包括图元装配和裁剪,然后交给片元着色器处理;
    4. Fragment片元着色器:片元着色器计算纹理坐标颜色并填充像素点,将结果存入帧缓冲区;
    5. Framebuffer Operations帧缓冲区:处理图片透明度、模板、开启深度测试,最后进行颜色混合,从帧缓冲区读取数据并显示到屏幕。

    四、屏幕显示图像的流程

    1,顶点着色器

    •  着色器程序——输入顶点数据,描述顶点上执行操作的顶点着色器程序源代码/可执行文件;
    • 顶点着色器输入[属性attribute]——用顶点数组提供每个顶点的数据,attribute不能修饰片元着色器里的变量;
    • 统一变量[uniform]——顶点/片元着色器使用的不变数据,比如客户端代码中计算好的旋转矩阵,通过uniform传输到GLSL中让三角形的每一个顶点都应用旋转矩阵;
    • 采样器——处理纹理时的一个特殊的变量,获取纹理时不会传递纹理文件纹理数据或者纹理指针到GLSL,而是使用纹理ID在内存中查找对应的纹理。代表顶点着色器使用纹理的特殊统一变量类型;

    顶点着色器输出属性:

    顶点着色器实际就是获取到最终的结果给gl_Position赋值,使得计算变换之后的顶点生效,也可以对gl_PointSize赋值更改点的大小。

    顶点着色器功能:

    1. 矩阵变换位置
    2. 计算光照公式生成逐顶点颜色
    3. 生成/变换纹理坐标

    总结: 它可以用于执行自定义计算,实施新的变换,照明或者传统的固定功能所不允许的基于顶点的效果。

    attribute vec4 position; //attribute属性变量修饰符,vec4是数据类型表示四维向量,顶点坐标
    attribute vec2 texCoordinate; //vec2二维向量,纹理坐标是二维纹理
    uniform mat4 rotateMatrix; //uniform统一通道修饰符,mat4四行四列的矩阵,旋转矩阵
    varying lowp vec2 varyTexCoord; //纹理坐标传递到片元着色器,通过varying通道传递,lowp表示低精度
    void main() {
        varyTexCoord = texCoordinate; //赋值后传递给片元着色器
    
        vec4 vPos = position;
        vPos  = vPos * rotateMatrix; //每一个顶点与旋转矩阵相乘得到新的顶点
    
        gl_Position = vPos; //赋值最终计算好的新顶点,gl_Position是内建变量,不需要定义只需要赋值
    }

    GPU并行计算顶点,三角形的3个顶点并行计算,上面的代码并行执行3次

    2,图元装配

    顶点着色器之后,下一个阶段就是图元装配,确定图形的形状。

    三种图元(Primitive):点,线,三角形.

    图元装配:将顶点数据计算成⼀个个图元.在这个阶段会执⾏裁剪、透视分割和 Viewport变换操作,这个过程开发者无法干预。

    图元类型和顶点确定将被渲染的单独图元。对于每个单独图元及其对应的顶点,图元装配阶段执行的操作包括:将顶点着色器的输出值执行裁剪、透视分割、视角变换后进入光栅化阶段。

    3,光栅化

    确定图形在屏幕上的像素点

    在这个阶段绘制对应的图元(点/线/三⻆形),光栅化就是将图元转化成一组二维片段的过程。而这些转化的片段将由片元着色处理。这些二维片段就是屏幕上可绘制的像素.

    4,片元着色器

    • 着色器程序——描述片段上执行操作的片元着色器程序源代码/可执行文件
    • 输入变量——由顶点着色器传递进来 ,片元着色器没有属性attribute变量。光栅化单元用插值为每个片段生成的顶点着色器输出
    • 统一变量(uniform)——顶点/片元着色器使用的不变数据,比如旋转矩阵
    • 采样器——代表片元着色器使用纹理的特殊统一变量类型。获取到纹理ID从内存中查找对应的纹理

    片元着色器输出颜色:

    片元着色器是输出颜色,通过计算颜色混合等最终输出一个像素点的颜色赋值给gl_FragColor,让对应的这一个像素点得到一种颜色。

    片元着色器功能:

    1. 计算颜色
    2. 获取纹理值,从图片中获取对应像素点的颜色值
    3. 往像素点中填充颜色值(纹理值/颜色值);

    总结: 它可以用于图片/视频/图形中每个像素的颜色填充(比如给视频添加滤镜,实际上就是将视频中每个图片的像素点颜色填充进行修改.)

    varying lowp vec2 varyTexCoord; //从顶点着色器传过来的纹理坐标,需要与顶点着色器的定义完全一致
    uniform sampler2D colorMap; //由客户端通过uniform传递过来的纹理采样器,拿到对应的纹理颜色值,取得纹素
    void main() {
        //texture2D(纹理采样器, 纹理坐标);获取对应位置对应坐标上的颜色值,取得纹素
        gl_FragColor = texture2D(colorMap, varyTexCoord); //gl_FragColor内建变量赋值,类型是vec4四维向量
    }

    片元着色器执行次数跟图片像素点的个数一样,并行执行每一个像素点获取颜色,让每一个像素点得到颜色值。

    5,逐片段操作

    片元着色器取出颜色后,进行逐片段操作

     拿到片段数据之后执行像素归属测试

    • 像素归属测试:确定帧缓存区中位置(Xw,Yw)的像素目前是不是归属于OpenGL ES所有。 例如,如果一个显示OpenGL ES帧缓存区View被另外一个View 所遮蔽。则窗口系统可以确定被遮蔽的像素不属于OpenGL ES上下文,从而不全显示这些像素。像素归属测试是OpenGL ES 的一部分,它不由开发者人为控制,而是由OpenGL ES 内部进行。
    • 裁剪测试:裁剪测试确定(Xw,Yw)是否位于作为OpenGL ES状态的一部分裁剪矩形范围内。如果该片段位于裁剪区域之外,则被抛弃。
    • 深度测试:输入片段的深度值进一步比较,确定片段是否拒绝测试
    • 混合:混合将新生成的片段颜色与保存在帧缓存的位置的颜色值组合起来。与片元着色器的颜色混合不同
    • 抖动:抖动可用于最小化因为使用有限精度在帧缓存区中保存颜色值而产生的伪像。

    五、OpenGL 上下文

    OpenGL 是一个仅仅关注图像渲染的图像接口库,在渲染过程中它需要将顶点信息、纹理信息、编译好的着色器等渲染状态信息存储起来,而存储这些信息的数据结构就可以看作 OpenGL 的上下文。

    调用任何 OpenGL 函数前,必须已经创建了 OpenGL Context,GL Context 存储了OpenGL 的状态变量以及其他渲染有关的信息。OpenGL 是个状态机,有很多状态变量,是个标准的过程式操作过程,改变状态会影响后续所有操作,这和面向对象的解耦原则不符,毕竟渲染本身就是个复杂的过程。OpenGL 采用 Client-Server 模型来解释 OpenGL 程序,即 Server 存储 GL Context(可能不止一个),Client 提出渲染请求,Server 给予响应,一般 Server 和 Client 都在我们的 PC 上,但 Server 和 Client 也可以是通过网络连接。

    之后的渲染工作就要依赖这些渲染状态信息来完成,当一个上下文被销毁时,它所对应的 OpenGL 渲染工作也将结束。

    六、EGL(Embedded Graphics Library)

    在 OpenGL 的设计中,OpenGL 是不负责管理窗口的,窗口的管理交由各个设备自己来完成,具体来讲,IOS 平台上使用 EAGL 提供本地平台对 OpenGL 的实现,在 Android 平台上使用 EGL 提供本地平台对 OpenGL 的实现。EGL 是 OpenGL ES 和 Android 底层平台视窗系统之间的接口,在 OpenGL 的输出与设备屏幕之间架接起一个桥梁,承担了为 OpenGL 提供上下文环境以及管理窗口的职责。

    EGL 为双缓冲工作模式,即有一个 Back Frame Buffer 和一个 Front Frame Buffer,正常绘制的目标都是 Back Frame Buffer,绘制完成后再调用 eglSwapBuffer API,将绘制完毕的 FrameBuffer 交换到 Front Frame Buffer 并显示出来。

    从代码层面来看,OpenGL ES 的 opengles 包下定义了平台无关的绘图指令,EGL(javax.microedition.khronos.egl)则定义了控制 displays,contexts 以及 surfaces 的统一的平台接口。

    • Display(EGLDisplay) 是对实际显示设备的抽象
    • Surface(EGLSurface)是对用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer、Stencil Buffer、Depth Buffer
    • Context(EGLContext)存储 OpenGL ES 绘图的一些状态信息

    展开全文
  • 相关API介绍2.1创建着色器对象 gl.createShader()2.2指定着色器代码 gl.shaderSource() 1.initShaders() 函数创建步骤 从第一个demo开始一直使用了一个着色器初始化函数initShaders(),但是一直没有详细说明,接下来...

    1.initShaders() 函数创建步骤

    从第一个demo开始一直使用了一个着色器初始化函数initShaders(),但是一直没有详细说明,接下来我们了解一下它是怎么把字符串形式的 GLSL ES 代码编译成可在GPU上运行的着色器程序,该函数大致可分为以下七个步骤:

    1. 创建着色器对象–gl.createShader()
    2. 向着色器对象中填充着色器程序源代码–gl.createSource()
    3. 编译着色器–gl.compileShader()
    4. 创建程序对象–gl.createProgram()
    5. 为程序对象分配着色器–gl.attachShader()
    6. 连接程序对象–gl.linkProgram()
    7. 使用程序对象–gl.useProgram()

    在以上七个步骤中使用到了两种对象:着色器对象程序对象
    它们之间的关系是:程序对象着色器对象 的容器,而着色器对象又管理者两个着色器,一个是顶点着色器,一个是片元着色器

    2.相关API介绍

    2.1创建着色器对象 gl.createShader()

    在WebGL中所有的着色器对象必须通过调用 gl.createShader() 来创建

    
    调用示例:gl.createShader(type)
    --------------------------------------------------------------------------
    函数功能:创建由type指定的着色器对象
    --------------------------------------------------------------------------			
    参数		
    			type					指定创建着色器的类型,可以是以下中的一个
    			gl.VERTEX_SHADER		表示顶点着色器
    			gl.FRAGMENT_SHADER		表示片元着色器
    --------------------------------------------------------------------------		
    返回值		非null					新创建的着色器
    			null					创建失败
    --------------------------------------------------------------------------
    错误		INVALID_ENUM			type不是上述值之一
    

    gl.createShader()根据传入的type创建顶点着色器或者片元着色器,如果不在需要这个着色器了,可以 像这样 gl.deleteShader(shader) 删除它,
    注:删除着色器的时候,如果着色器对象还在使用(使用gl.attachShader()函数使这个着色器附件在了程序对象上),那么调用删除函数后,不会立即删除着色器,要等到不在使用该着色器时才会删除掉

    2.2指定着色器代码 gl.shaderSource()

    在javaScript程序中,源代码是以字符串的形式存储的,可以通过gl.shaderSouce()函数向着色器指定源码

    
    调用示例:gl.shaderSource(shader,source)
    --------------------------------------------------------------------------
    函数功能:将source指定的字符串形式的代码传入shader指定的着色器,如果已经向shader
    中传入过代码了,旧代码会被新代码替换掉
    --------------------------------------------------------------------------			
    参数		
    			shader		指定需要传入代码的着色器对象
    			source		指定字符串形式的代码
    --------------------------------------------------------------------------		
    返回值		无
    --------------------------------------------------------------------------
    错误		无
    

    2.3编译着色器 gl.compileShader()

    GLSL ES 语言与C或C++相似,需要编译成二进制的可执行文件才能使用,对于我们创建的着色器,在向着色器对象传入源代码之后,同样需要进行编译才能使用,WebGL 提供了gl.compileShader()函数,用来编译着色器
    注:如果调用gl.shaderSource() 函数时,用新代码替换了旧代码,已编译好的可执行文件不会被自动替换掉,需要手动重新编译,即再次调用gl.compileShader()函数

    
    调用示例:gl.compileShader(shader)
    --------------------------------------------------------------------------
    函数功能:编译shader指定的着色器中的源代码
    --------------------------------------------------------------------------			
    参数		shader		指定要编译的着色器
    --------------------------------------------------------------------------		
    返回值		无
    --------------------------------------------------------------------------
    错误		无
    

    2.4获取着色器信息 gl.getShaderParameter()

    
    调用示例:gl.getShaderParameter(shader, pname)
    --------------------------------------------------------------------------
    函数功能:  获取shader指定的着色器中,pname指定的参数信息
    --------------------------------------------------------------------------			
    参数		
    			shader				指定待获取参数的着色器
    			pname				指定待获取参数的类型,可以是
    								gl.SHADER_TYPE、gl.DELETE_STATUS或者
    								gl.COMPILE_STATUS
    --------------------------------------------------------------------------		
    返回值		根据pname不同返回不同的值
    			gl.SHADER_TYPE		返回是顶点着色器(gl.VERTEX_SHADER)
    								还是片元着色器(gl.FRAMENT_SHADER)
    			gl.DELETE_STATUS	返回着色器是否被删除成功(truefalse)
    			gl.COMPILE_STATUS	返回着色器是否被编译成功(truefalse--------------------------------------------------------------------------
    错误		INVALID_ENUM		pname值无效
    

    2.5获取写入着色器日志 gl.getShaderInfoLog()

    
    调用示例:gl.getShaderInfoLog(shader)
    --------------------------------------------------------------------------
    函数功能:  获取shader指定的着色器的信息日志
    --------------------------------------------------------------------------			
    参数		shader				指定待获取参数的着色器
    --------------------------------------------------------------------------		
    返回值		non-null			包含日志信息的字符串
    			null				没有编译错误			
    --------------------------------------------------------------------------
    错误		无
    

    通常 gl.getShaderParameter() 函数和 gl.getShaderInfoLog() 函数是配合使用的,一般是将gl.getShaderParameter()函数的参数pname指定为 gl.COMPILE_STATUS 就可以检查着色器是否编译成功,如果编译失败通过 gl.getShaderInfoLog() 函数获取错误信息
    示例如下:

    ...
    //编译顶点、片元着色器
    gl.compileShader(vertexShader);
    gl.compileShader(fragmentShader);
    
    //检查顶点着色器是否编译成功
    var vertexShaderStatus = gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS);
    if (!vertexShaderStatus) {
      var error = gl.getShaderInfoLog(vertexShader);
      console.log('编译顶点着色器失败: ' + error);
      gl.deleteShader(vertexShader);
      return null;
    }
    //检查片元着色器是否编译成功
    var fragmentShaderStatus = gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS);
    if (!fragmentShaderStatus) {
      var error = gl.getShaderInfoLog(fragmentShader);
      console.log('编译片元着色器失败: ' + error);
      gl.deleteShader(fragmentShader);
      return null;
    }
    ...
    

    2.6创建程序对象 gl.createProgram()

    
    调用示例:gl.createProgram()
    --------------------------------------------------------------------------
    函数功能:创建程序对象
    --------------------------------------------------------------------------			
    参数		无
    --------------------------------------------------------------------------		
    返回值		non-null	新创建的程序对象
    			null		创建失败
    --------------------------------------------------------------------------
    错误		无
    

    2.7删除程序对象 gl.deleteProgram()

    
    调用示例:gl.deleteProgram(program)
    --------------------------------------------------------------------------
    函数功能:创建程序对象
    --------------------------------------------------------------------------			
    参数		program		要删除的程序对象
    --------------------------------------------------------------------------		
    返回值		无
    --------------------------------------------------------------------------
    错误		无
    

    2.8为程序对象分配着色器对象 gl.attachShader()

    WebGL程序要运行起来,必须要有两个着色器,一个顶点着色器,一个片元着色器,就需要通过gl.attachShader() 函数为程序对象分配这两个着色器

    
    调用示例:gl.attachShader(program, shader)
    --------------------------------------------------------------------------
    函数功能:将shader指定的着色器对象分配给program指定的程序对象
    --------------------------------------------------------------------------			
    参数		program				指定程序对象
    			shader				指定着色器对象
    --------------------------------------------------------------------------		
    返回值		无
    --------------------------------------------------------------------------
    错误		INVALID_OPERATION	shader已经被分配给program
    

    2.9取消程序对象分配的着色器对象 gl.detachShader()

    
    调用示例:gl.detachShader(program, shader)
    --------------------------------------------------------------------------
    函数功能:取消shader指定的着色器对象对program指定的程序对象的分配
    --------------------------------------------------------------------------			
    参数		program				指定程序对象
    			shader				指定着色器对象
    --------------------------------------------------------------------------		
    返回值		无
    --------------------------------------------------------------------------
    错误		INVALID_OPERATION	shader没有被分配给program
    

    2.10连接程序对象 gl.linkProgram()

    为程序对象分配了两个着色器对象后,还需要将它们与程序对象连接起来,使用 gl.linkProgram() 函数完成这一操作

    
    调用示例:gl.linkProgram(program)
    --------------------------------------------------------------------------
    函数功能:连接program指定的程序对象中的着色器
    --------------------------------------------------------------------------			
    参数		program				指定程序对象
    --------------------------------------------------------------------------		
    返回值		无
    --------------------------------------------------------------------------
    错误		无
    

    程序对象进行着色器连接操作,目的是检查以下四项:

    1. 顶点着色器和片元着色器中的varying变量同名同类型,且一一对应
    2. 顶点着色器中的每个varying变量都已经赋值
    3. 顶点着色器和片元着色器中的同名uniform变量也是同类型,无需一一对应
    4. 着色器中的attribute变量、uniform变量、varying变量个数有没有超过着色器的上限

    2.11检查着色器是否连接成功 gl.getProgramParameter()

    
    调用示例:gl.getProgramParameter(program, pname)
    --------------------------------------------------------------------------
    函数功能:  获取program指定的程序对象中,pname指定的参数信息
    --------------------------------------------------------------------------			
    参数		
    			program				指定待获取参数的程序对象
    			pname				指定待获取参数的类型,可以是
    								gl.DELETE_STATUS、gl.LINK_STATUS、
    								gl.VALIDATE_STATUS、gl.ATTACHED_SHADERS、
    								gl.ACTIVE_ATTRIBUTES、gl.ACTIVE_UNIFORM
    --------------------------------------------------------------------------		
    返回值		根据pname不同返回不同的值
    			gl.DELETE_STATUS	程序是否被删除(truefalse)
    			gl.LINK_STATUS		程序是否已经成功连接(truefalse)
    			gl.VALIDATE_STATUS	程序是否已经通过验证(truefalse)	
    			gl.ATTACHED_SHADERS	已被分配给程序的着色器数量
    			gl.ACTIVE_ATTRIBUTES 	顶点着色器中attribute变量的数量
    			gl.ACTIVE_UNIFORM	程序中uniform变量的数量	
    			
    --------------------------------------------------------------------------
    错误		INVALID_ENUM		pname值无效
    

    如果程序已经连接成功,我们就得到一个二进制的可执行模块供WebGL调用,如果连接失败可以通过 gl.getProgramInfoLog() 函数获取连接出错的信息

    2.12获取程序对象的日志 gl.getProgramInfoLog()

    
    调用示例:gl.getProgramInfoLog(program)
    --------------------------------------------------------------------------
    函数功能:  获取program指定的着色器的信息日志
    --------------------------------------------------------------------------			
    参数		program		指定待获取参数的程序对象
    --------------------------------------------------------------------------		
    返回值		包含日志信息的字符串		
    --------------------------------------------------------------------------
    错误		无
    
    ...
    //链接program
    gl.linkProgram(program);
    
    //检查程序对象是否连接成功
    var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (!linked) {
      var error = gl.getProgramInfoLog(program);
      console.log('Failed to link program: ' + error);
      gl.deleteProgram(program);
      gl.deleteShader(fragmentShader);
      gl.deleteShader(vertexShader);
      return null;
    }
    ...
    

    2.13使用程序对象 gl.useProgram()

    最后需要调用 gl.useProgram() 函数告知WebGL系统绘制时使用哪个程序对象,有了这个函数,我们就可以在绘制前准备多个程序对象,在绘制的时候根据需要切换程序对象

    
    调用示例:gl.useProgram(program)
    --------------------------------------------------------------------------
    函数功能:  告知WebGL系统绘制时使用program指定的程序对象
    --------------------------------------------------------------------------			
    参数		program		指定待使用的程序对象
    --------------------------------------------------------------------------		
    返回值		无
    --------------------------------------------------------------------------
    错误		无
    

    3.initShaders代码

    //初始化着色器函数
    function initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE) {
      //创建顶点着色器对象
      var vertexShader = loadShader(gl, gl.VERTEX_SHADER, VSHADER_SOURCE);
      //创建片元着色器对象
      var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, FSHADER_SOURCE);
    
      if (!vertexShader || !fragmentShader) {
        return null;
      }
    
      //创建程序对象program
      var program = gl.createProgram();
      if (!gl.createProgram()) {
        return null;
      }
      //分配顶点着色器和片元着色器到program
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      //链接program
      gl.linkProgram(program);
    
      //检查程序对象是否连接成功
      var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
      if (!linked) {
        var error = gl.getProgramInfoLog(program);
        console.log('Failed to link program: ' + error);
        gl.deleteProgram(program);
        gl.deleteShader(fragmentShader);
        gl.deleteShader(vertexShader);
        return null;
      }
      //使用program
      gl.useProgram(program);
      gl.program = program
      //返回程序program对象
      return program;
    }
    
    function loadShader(gl, type, source) {
      // 创建顶点着色器对象
      var shader = gl.createShader(type);
      if (shader == null) {
        console.log('创建着色器失败');
        return null;
      }
    
      // 引入着色器源代码
      gl.shaderSource(shader, source);
    
      // 编译着色器
      gl.compileShader(shader);
    
      // 检查顶是否编译成功
      var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
      if (!compiled) {
        var error = gl.getShaderInfoLog(shader);
        console.log('编译着色器失败: ' + error);
        gl.deleteShader(shader);
        return null;
      }
    
      return shader;
    }
    

    4 后续

    使用修改后的initShaders函数,可以在浏览器中调试着色器代码,如果出错,会在控制台上输出出错代码的行数,如下图,它会告诉你错误出现在第4行,在uniform11附件,这时你差不多能知道是你将uniform写成uniform11了
    在这里插入图片描述

    展开全文
  • Shader入门—曲面细分着色器和几何着色器 前记:学不可以停止-------------------------------mx 基础知识: 曲面细分着色器:可以将一个几何体细化为一个球体也能将一根直线无限向曲线逼近 使用曲面细分的好处:...

    Shader入门—曲面细分着色器和几何着色器
    前记:学不可以停止-------------------------------mx

    基础知识:
    曲面细分着色器:可以将一个几何体细化为一个球体也能将一根直线无限向曲线逼近

    使用曲面细分的好处:直接使用顶点数更多的模型会带来更高的性能消耗,曲面细分能根据规则来动态调整模型的复杂度,达到对应的效果

    曲面细分着色器的输入输出:

    1. 输入:Patch,可以看成是多个顶点的集合,包含每个顶点的属性,可以指定一个Patch包含的顶点数以及自己的属性
    2. 功能:将图元细分(可以是三角形、矩形等
    3. 输出:细分后的顶点

    曲面细分着色器流程:

    1. Hull Shader : 可以编程,决定细分数量(设定Tessellation factor以及Inside Tessellation factor),对输入的Patch参数进行改变(如果需要)
    2. Tessellation Primitive Generation :无法编程,进行细分操作
    3. Domain Shader : 可以编程,对细分后的点进行处理,从重心空间(Barycentric coordi)转换到屏幕空间

    Hull Shader各参数解析:

    几何着色器:专门处理场景里的几何图形,可以将创建或销毁几何图元,可以根据顶点的信息批量处理几何图形,对顶点附近的数据进行函数的处理,快速创造出新的多边形(Vertex Shader是专门处理多边形顶点的,而Geometry shader就是专门用来处理场景中的几何图形)

    几何着色器的输入输出:

    1. 输入:图元—顶点,边,三角形等
      points-GL_POINTS (1)
      lines-GL_LINES, GL_LINE_STRIP, GL_LINE_LIST (2)
      lines_adjacency-GL_LINES_ADJACENCY, GL_LINE_STRIP_ADJACENCY (4)
      triangles-GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN(3)
      triangles_adjacency-GL_TRIANGLES_ADJACENCY, GL_TRIANGLE_STRIP_ADJACENCY (6)

    2. 输出:图元(输出图元类型跟输入图元类型完全不同,而且输出图元的数量和输入图元数量也没有关系)
      points
      line_strip
      triangle_strip

    几何着色器的实际应用:

    1. 法线可视化
    2. 动态几何体形成
    3. 草地的生成

    曲面细分着色器shader

    Shader "Custom/TessShader"
    {
        Properties
        {
            _Color ("Color", Color) = (1,1,1,1)
            _MainTex ("Albedo (RGB)", 2D) = "white" {}
            _TessellationUniform ("_TessellationUniform", Range(1,64)) = 1
    
        }
        SubShader
        {
            Name "FORWARD"
            Tags 
            { 
            "LightMode"="ForwardBase"
            }
            LOD 200
            Pass{
            CGPROGRAM
            #pragma hull hullProgram
            #pragma domain ds
            #pragma vertex tessvert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Tessellation.cginc"
            // Use shader model 3.0 target, to get nicer looking lighting
            #pragma target 5.0
    
            sampler2D _MainTex;
            float4 _MainTex_ST;
            struct VertexInput
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL ;
                float4 tangent : TANGENT ;
                float2 uv : TEXCOORD;
            };
            struct VertexOutput
            {
                float4 vertex : SV_POSITION;
                float3 normal :NORMAL;
                float4 tangent :TANGENT;
                float2 uv : TEXCOORD;
            };
    
            VertexOutput vert  (VertexInput v)//这个函数应用在domain函数中,用来处理坐标
            {
                VertexOutput o;
                o.uv =TRANSFORM_TEX(v.uv,_MainTex);
                o.vertex =UnityObjectToClipPos(v.vertex);
                o.normal =v.normal;
                o.tangent = v.tangent;
                return o;
            }
            #ifdef UNITY_CAN_COMPILE_TESSELLATION
                struct TessVertex
                {
                    float4 vertex: INTERNALTESSPOS;
                    float3 normal :NORMAL;
                    float4 tangent :TANGENT;
                    float2 uv :TEXCOORD0;
                };
                struct OutputPatchConstant//不同图元该结构会有所不同
                {
                    float edge[3]  : SV_TESSFACTOR;
                    float inside : SV_INSIDETESSFACTOR;
                };
                TessVertex tessvert (VertexInput v)
                {
                    TessVertex o;
                    o.vertex = v.vertex;
                    o.normal = v.normal;
                    o.tangent=v.tangent;
                    o.uv = v.uv;
                    return o;
                }
                float _TessellationUniform;
                OutputPatchConstant hsconst (InputPatch<TessVertex , 3> patch)
                {
                    OutputPatchConstant o;
                    o.edge[0] = _TessellationUniform;
                    o.edge[1] = _TessellationUniform;
                    o.edge[2] = _TessellationUniform;
                    o.inside = _TessellationUniform;
                    return o;
                }
                [UNITY_domain("tri")]//去掉图元
                [UNITY_partitioning("fractional_odd")]
                [UNITY_outputtopology("triangle_cw")]
                [UNITY_patchconstantfunc("hsconst")]//规定曲面细分的属性,一个patch有三个点,但是三个点都会共用这个函数
                [UNITY_outputcontrolpoints(3)]//不同的图元会对应不同的控制点
                TessVertex hullProgram (InputPatch<TessVertex,3> patch ,uint id:SV_OutputControlPointID)
                {
                    return patch[id];
                }
                [UNITY_domain("tri")]//同样需要定义图元
                VertexOutput ds (OutputPatchConstant tessFactors ,const OutputPatch<TessVertex,3> patch ,float3 bary :SV_DOMAINLOCATION)
                {
                    VertexOutput v;
                    v.vertex = patch[0].vertex * bary.x + patch[1].vertex*bary.y +patch[2].vertex*bary.z;
                    v.tangent =patch[0].tangent*bary.x +patch[1].tangent*bary.y +patch[2].tangent*bary.z;
                    v.normal =patch[0].normal*bary.x + patch[1].normal * bary.y +patch[2].normal*bary.z;
                    v.uv =patch[0].uv*bary.x + patch[1].uv *bary.y +patch[2].uv * bary.z;
                    VertexOutput o =vert(v);
                    return o;
                }
                #endif
                float4 frag(VertexOutput i) :SV_Target
                {
                    return float4(1.0,1.0,1.0,1.0);
                }
                
             ENDCG
             }
        }
        FallBack "Diffuse"
    }
    
    

    在这里插入图片描述

    曲面细分着色器shader与置换贴图结合

    Shader "Unlit/Tess_Diss_Shader"
    {
        Properties
        {
            _MainTex("MainTex",2D) = "white"{}
            _DisplacementMap("_DisplacementMap",2D)="gray"{}
            _DisplacementStrength("DisplacementStrength",Range(0,1)) = 0
            _Smoothness("Smoothness",Range(0,5))=0.5
            _TessellationUniform("TessellationUniform",Range(1,64)) = 1
        }
        SubShader
        {
            Tags { "RenderType"="Opaque" 
                   "LightMode"="ForwardBase"}
            LOD 100
            Pass
            {
                CGPROGRAM
                //定义2个函数 hull domain
                #pragma hull hullProgram
                #pragma domain ds
               
                #pragma vertex tessvert
                #pragma fragment frag
    
                #include "UnityCG.cginc"
                #include "Lighting.cginc"
                //引入曲面细分的头文件
                #include "Tessellation.cginc" 
    
                #pragma target 5.0
                float _TessellationUniform;
                sampler2D _MainTex;
                float4 _MainTex_ST;
    
                sampler2D _DisplacementMap;
                float4 _DisplacementMap_ST;
                float _DisplacementStrength;
                float _Smoothness;
    
                struct VertexInput
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                    float3 normal : NORMAL;
                    float4 tangent : TANGENT;
                };
    
                struct VertexOutput
                {
                    float2 uv : TEXCOORD0;
                    float4 pos : SV_POSITION;
                    float4 worldPos:TEXCOORD1;
                    half3 tspace0 :TEXCOORD2;
                    half3 tspace1 :TEXCOORD3;
                    half3 tspace2 :TEXCOORD4;
                };
    
                VertexOutput vert (VertexInput v)
                //这个函数应用在domain函数中,用来空间转换的函数
                {
                    VertexOutput o;
                    o.uv = TRANSFORM_TEX(v.uv,_MainTex);
                    //Displacement
                    //由于并不是在Fragnent shader中读取图片,GPU无法获取mipmap信息,因此需要使用tex2Dlod来读取图片,使用第四坐标作为mipmap的level,这里取了0
                    float Displacement = tex2Dlod(_DisplacementMap,float4(o.uv.xy,0.0,0.0)).g;
                    Displacement = (Displacement-0.5)*_DisplacementStrength;
                    v.normal = normalize(v.normal);
                    v.vertex.xyz += v.normal * Displacement;
    
                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.worldPos = mul(unity_ObjectToWorld, v.vertex);
    
                    //计算切线空间转换矩阵
                    half3 vNormal = UnityObjectToWorldNormal(v.normal);
                    half3 vTangent = UnityObjectToWorldDir(v.tangent.xyz);
                    //compute bitangent from cross product of normal and tangent
                    half tangentSign = v.tangent.w * unity_WorldTransformParams.w;
                    half3 vBitangent = cross(vNormal,vTangent)*tangentSign;
                    //output the tangent space matrix
                    o.tspace0 = half3(vTangent.x,vBitangent.x,vNormal.x);
                    o.tspace1 = half3(vTangent.y,vBitangent.y,vNormal.y);
                    o.tspace2 = half3(vTangent.z,vBitangent.z,vNormal.z);
                    return o;
                }
    
                //有些硬件不支持曲面细分着色器,定义了该宏就能够在不支持的硬件上不会变粉,也不会报错
                #ifdef UNITY_CAN_COMPILE_TESSELLATION
                    //顶点着色器结构的定义
                    struct TessVertex{
                        float4 vertex : INTERNALTESSPOS;
                        float3 normal : NORMAL;
                        float4 tangent : TANGENT;
                        float2 uv : TEXCOORD0;
                    };
    
                    struct OutputPatchConstant { 
                        //不同的图元,该结构会有所不同
                        //该部分用于Hull Shader里面
                        //定义了patch的属性
                        //Tessellation Factor和Inner Tessellation Factor
                        float edge[3] : SV_TESSFACTOR;
                        float inside  : SV_INSIDETESSFACTOR;
                    };
    
                    TessVertex tessvert (VertexInput v){
                        //顶点着色器函数
                        TessVertex o;
                        o.vertex  = v.vertex;
                        o.normal  = v.normal;
                        o.tangent = v.tangent;
                        o.uv      = v.uv;
                        return o;
                    }
    
                    //float _TessellationUniform;
                    OutputPatchConstant hsconst (InputPatch<TessVertex,3> patch){
                        //定义曲面细分的参数
                        OutputPatchConstant o;
                        o.edge[0] = _TessellationUniform;
                        o.edge[1] = _TessellationUniform;
                        o.edge[2] = _TessellationUniform;
                        o.inside  = _TessellationUniform;
                        return o;
                    }
    
                    [UNITY_domain("tri")]//确定图元,quad,triangle等
                    [UNITY_partitioning("fractional_odd")]//拆分edge的规则,equal_spacing,fractional_odd,fractional_even
                    [UNITY_outputtopology("triangle_cw")]
                    [UNITY_patchconstantfunc("hsconst")]//一个patch一共有三个点,但是这三个点都共用这个函数
                    [UNITY_outputcontrolpoints(3)]      //不同的图元会对应不同的控制点
                  
                    TessVertex hullProgram (InputPatch<TessVertex,3> patch,uint id : SV_OutputControlPointID){
                        //定义hullshaderV函数
                        return patch[id];
                    }
    
                    [UNITY_domain("tri")]//同样需要定义图元
                    VertexOutput ds (OutputPatchConstant tessFactors, const OutputPatch<TessVertex,3>patch,float3 bary :SV_DOMAINLOCATION)
                    //bary:重心坐标
                    {
                        VertexInput v;
                        v.vertex = patch[0].vertex*bary.x + patch[1].vertex*bary.y + patch[2].vertex*bary.z;
    			        v.tangent = patch[0].tangent*bary.x + patch[1].tangent*bary.y + patch[2].tangent*bary.z;
    			        v.normal = patch[0].normal*bary.x + patch[1].normal*bary.y + patch[2].normal*bary.z;
    			        v.uv = patch[0].uv*bary.x + patch[1].uv*bary.y + patch[2].uv*bary.z;
    
                        VertexOutput o = vert (v);
                        return o;
                    }
                #endif
    
                float4 frag (VertexOutput i) : SV_Target
                {
                    float3 lightDir =_WorldSpaceLightPos0.xyz;
                    float3 tnormal = UnpackNormal (tex2D (_DisplacementMap, i.uv));
                    half3 worldNormal;
                    worldNormal.x=dot(i.tspace0,tnormal);
                    worldNormal.y= dot (i.tspace1, tnormal);
                    worldNormal.z=dot (i.tspace2, tnormal);
                    float3 albedo=tex2D (_MainTex, i.uv). rgb;
                    float3 lightColor = _LightColor0.rgb;
                    float3 diffuse = albedo * lightColor * DotClamped(lightDir,worldNormal);
                    float3 viewDir = normalize (_WorldSpaceCameraPos. xyz-i. worldPos. xyz);
                    float3 halfVector = normalize(lightDir + viewDir);
                    float3 specular = albedo * pow (DotClamped (halfVector, worldNormal), _Smoothness * 100);
                    float3 result = specular + diffuse;
                    return float4(result, 1.0);
    
                    return float4(result,1.0);
                }
                ENDCG
            }
        }
        Fallback "Diffuse"
    }
    

    在这里插入图片描述

    几何着色器shader
    _DissolveThreshold、_ColorFactor的值需要根据不同的模型修改

    Shader "My/DoublePass"
    {
     
    Properties
    {
    		_Diffuse("Diffuse", Color) = (1,1,1,1)
    		_DissolveColor("Dissolve Color", Color) = (0,0,0,0)
    		_MainTex("Base 2D", 2D) = "white"{}
    		_ColorFactor("ColorFactor", Range(0,0.001)) = 0.0004
    		_DissolveThreshold("DissolveThreshold", Range(-0.002,0.02)) = 0  
    		_BoxScale("BoxScale",Range(0,1)) = 0.001
    		_BoxColor("BoxColor",Color) = (1,1,1,1)
    		_Alpha("_Alpha",Range(0,1))=1
    }
     
    SubShader
    {
    Pass{
    Tags{ "RenderType" = "Opaque" } 
    	Cull Off
    	CGPROGRAM
    	#pragma vertex vert
    	#pragma fragment frag	
     
    	#include "Lighting.cginc"
    	uniform fixed4 _Diffuse;
    	uniform fixed4 _DissolveColor;
    	uniform sampler2D _MainTex;
    	uniform float4 _MainTex_ST;
    	uniform float _ColorFactor;
    	uniform float _DissolveThreshold;  
    	
    	struct v2f
    	{
    		float4 pos : SV_POSITION;
    		float3 worldNormal : TEXCOORD0;
    		float2 uv : TEXCOORD1;
    		float4 objPos : TEXCOORD2; 
    	};
    	
    	v2f vert(appdata_base v)
    	{
    		v2f o;
    		o.pos = UnityObjectToClipPos(v.vertex);
    		o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    		o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
    		o.objPos = v.vertex;  
    		return o;
    	}
    	
    	fixed4 frag(v2f i) : SV_Target
    	{
    		float factor = i.objPos.z -_DissolveThreshold ;
    		clip(factor); 
    		
    		fixed3 worldNormal = normalize(i.worldNormal);
    		fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
    		fixed3 lambert = saturate(dot(worldNormal, worldLightDir));
    		fixed3 albedo = lambert * _Diffuse.xyz * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz;
    		fixed3 color = tex2D(_MainTex, i.uv).rgb * albedo;
     
    		if (factor < _ColorFactor)
    		{
    			return _DissolveColor;
    		}
    		return fixed4(color, 1);
    	}
    	ENDCG
    }
     
    
     
    Pass {
    Tags { "RenderType"="Transparent" "Queue"="Transparent" }
    	  Blend One One
          Cull Off
     
          CGPROGRAM
          #pragma target 4.0
          #pragma vertex vert
          #pragma geometry geo
          #pragma fragment frag
          #pragma multi_compile_fog
          #include "UnityCG.cginc"
     
    			struct appdata
    			{
    				float4 vertex : POSITION;
    				float2 uv : TEXCOORD0;
    				float3 normal : NORMAL;
    			};
     
    			struct v2g
    			{
    				float4 vertex : POSITION;
    				float3 normal : NORMAL;
    				float2 uv : TEXCOORD0;
    				float4 objPos: TEXCOORD1;
    			};
     
    			struct g2f
    			{
    				float2 uv : TEXCOORD0;
    				float4 vertex : SV_POSITION;
    			};
     
          sampler2D _MainTex;
    	  float4 _MainTex_ST;
          float _BoxScale;
    	  fixed4 _BoxColor;
    	  float _ColorFactor;
    	  float _DissolveThreshold; 
    	  float _Alpha;
     
          v2g vert (appdata v) {
            v2g o;
            o.vertex = v.vertex;
            o.uv  = TRANSFORM_TEX(v.uv, _MainTex);
    		o.objPos = v.vertex;
            return o;
          }
     
     
          #define ADD_VERT(v) \
            o.vertex = UnityObjectToClipPos(v); \
            TriStream.Append(o);
          
          #define ADD_TRI(p0, p1, p2) \
            ADD_VERT(p0) ADD_VERT(p1) \
            ADD_VERT(p2) \
            TriStream.RestartStrip();
     
          
          [maxvertexcount(36)]
          void geo(triangle v2g v[3], inout TriangleStream<g2f> TriStream) { 
     
            float4 vertex = (v[0].vertex + v[1].vertex + v[2].vertex) / 3;
            float2 uv = (v[0].uv + v[1].uv + v[2].uv) / 3;
     
    		float3 edgeA = v[1].vertex - v[0].vertex;
    		float3 edgeB = v[2].vertex - v[0].vertex;
    		float3 normalFace = normalize(cross(edgeA, edgeB));
    	 
     
    		float factor =  vertex.z - _DissolveThreshold;
    		//if(factor < 0) return;
    		if(factor < _ColorFactor &&factor+_ColorFactor*2 > _ColorFactor)
    		{
    			//这里存粹是为效果而设置的,让它看上去是对的。
    
    				vertex.xyz += normalFace * lerp(0,0.005,sin(_Time*0.5));
    			//不同的消散效果可以调不同的参数
    
    			g2f o;
    			o.uv = uv;
    			float scale = _BoxScale;
     
    			float4 v0 = float4( 1, 1, 1,1)*scale + float4(vertex.xyz,0);
    			float4 v1 = float4( 1, 1,-1,1)*scale + float4(vertex.xyz,0);
    			float4 v2 = float4( 1,-1, 1,1)*scale + float4(vertex.xyz,0);
    			float4 v3 = float4( 1,-1,-1,1)*scale + float4(vertex.xyz,0);
    			float4 v4 = float4(-1, 1, 1,1)*scale + float4(vertex.xyz,0);
    			float4 v5 = float4(-1, 1,-1,1)*scale + float4(vertex.xyz,0);
    			float4 v6 = float4(-1,-1, 1,1)*scale + float4(vertex.xyz,0);
    			float4 v7 = float4(-1,-1,-1,1)*scale + float4(vertex.xyz,0);
     
     
    			ADD_TRI(v0, v2, v3);
    			ADD_TRI(v3, v1, v0);
    			ADD_TRI(v5, v7, v6);
    			ADD_TRI(v6, v4, v5);
     
    			ADD_TRI(v4, v0, v1);
    			ADD_TRI(v1, v5, v4);
    			ADD_TRI(v7, v3, v2);
    			ADD_TRI(v2, v6, v7);
     
    			ADD_TRI(v6, v2, v0);
    			ADD_TRI(v0, v4, v6);
    			ADD_TRI(v5, v1, v3);
    			ADD_TRI(v3, v7, v5);
            }	  
    		else
    		{
    			vertex.xyz =float3(0,0,0);
    		}
          }
     
          fixed4 frag (g2f i) : SV_Target {
            float4 col = _BoxColor;
    		col.a = _Alpha;
            return col;
          }
          ENDCG
    }
        }
     }
    

    在这里插入图片描述

    学习相关链接:
    https://www.bilibili.com/video/BV1XX4y1A7Ns
    https://blog.csdn.net/qq_37925032/article/details/82936769
    https://www.yuque.com/docs/share/4941142e-6c9e-4f6d-8e53-ee053ef42a2e?#
    https://www.yuque.com/sugelameiyoudi-jadcc/okgm7e/xyx5h5
    https://zhuanlan.zhihu.com/p/76775024
    https://blog.csdn.net/vampirem/article/details/12152945?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%87%A0%E4%BD%95%E7%9D%80%E8%89%B2%E5%99%A8&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-7-.nonecase&spm=1018.2226.3001.4187

    ------------------------------博主:mx

    展开全文
  • OpenGL着色器基础

    2021-04-18 23:20:57
    本文翻译自LearnOpengl经典教程,OpenGL着色器基础介绍的比较通俗易懂,特总结分享一下! 为什么要使用着色器? 我们知道,OpenGL一般使用经典的固定渲染管线来渲染对象,但是随着OpenGL技术的不断发展,固定管线...

    前言:

    本文翻译自LearnOpengl经典教程,OpenGL着色器基础介绍的比较通俗易懂,特总结分享一下!

    为什么要使用着色器?

    我们知道,OpenGL一般使用经典的固定渲染管线来渲染对象,但是随着OpenGL技术的不断发展,固定管线技术也在不断改进,最终变成了当代的可编程管线技术。就是渲染管线的某些阶段可以通过编程来控制(提供了很大的灵活性),而着色器就是这些可编程的程序片段,用来替代原始管线的特定渲染阶段。

    着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。着色器的开头总是要声明版本,接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。如果你不知道什么是uniform也不用担心,我们后面会进行讲解。

    一个典型的着色器有下面的结构:

    当我们特别谈论到顶点着色器的时候,每个输入变量也叫顶点属性(Vertex Attribute)。我们能声明的顶点属性是有上限的,它一般由硬件来决定。OpenGL确保至少有16个包含4分量的顶点属性可用,但是有些硬件或许允许更多的顶点属性,你可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限:

    通常情况下它至少会返回16个,大部分情况下是够用了。

    数据类型

    和其他编程语言一样,GLSL有数据类型可以来指定变量的种类。GLSL中包含C等其它语言大部分的默认基础数据类型:int、float、double、uint和bool。GLSL也有两种容器类型,它们会在这个教程中使用很多,分别是向量(Vector)和矩阵(Matrix),其中矩阵我们会在之后的教程里再讨论。

    向量

    GLSL中的向量是一个可以包含有1、2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。它们可以是下面的形式(n代表分量的数量)

    大多数时候我们使用vecn,因为float足够满足大多数要求了。

    一个向量的分量可以通过vec.x这种方式获取,这里x是指这个向量的第一个分量。你可以分别使用.x.y.z.w来获取它们的第1、2、3、4个分量。GLSL也允许你对颜色使用rgba,或是对纹理坐标使用stpq访问相同的分量。

    向量这一数据类型也允许一些有趣而灵活的分量选择方式,叫做重组(Swizzling)。重组允许这样的语法:

    你可以使用上面4个字母任意组合来创建一个和原来向量一样长的(同类型)新向量,只要原来向量有那些分量即可;然而,你不允许在一个vec2向量中去获取.z元素。我们也可以把一个向量作为一个参数传给不同的向量构造函数,以减少需求参数的数量:

    向量是一种灵活的数据类型,我们可以把用在各种输入和输出上。学完教程你会看到很多新颖的管理向量的例子。

    输入与输出

    虽然着色器是各自独立的小程序,但是它们都是一个整体的一部分,出于这样的原因,我们希望每个着色器都有输入和输出,这样才能进行数据交流和传递。GLSL定义了inout关键字专门来实现这个目的。每个着色器使用这两个关键字设定输入和输出,只要一个输出变量与下一个着色器阶段的输入匹配,它就会传递下去。但在顶点和片段着色器中会有点不同。

    顶点着色器应该接收的是一种特殊形式的输入,否则就会效率低下。顶点着色器的输入特殊在,它从顶点数据中直接接收输入。为了定义顶点数据该如何管理,我们使用location这一元数据指定输入变量,这样我们才可以在CPU上配置顶点属性。我们已经在前面的教程看过这个了,layout (location = 0)。顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。

    你也可以忽略layout(location = 0)标识符,通过在OpenGL代码中使用glGetAttribLocation查询属性位置值(Location),但是我更喜欢在着色器中设置它们,这样会更容易理解而且节省你(和OpenGL)的工作量。

    另一个例外是片段着色器,它需要一个vec4颜色输出变量,因为片段着色器需要生成一个最终输出的颜色。如果你在片段着色器没有定义输出颜色,OpenGL会把你的物体渲染为黑色(或白色)。

    所以,如果我们打算从一个着色器向另一个着色器发送数据,我们必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了(这是在链接程序对象时完成的)。为了展示这是如何工作的,我们会稍微改动一下之前教程里的那个着色器,让顶点着色器为片段着色器决定颜色。

    输入与输出

      虽然着色器是各自独立的小程序,但是它们都是一个整体的一部分,出于这样的原因,我们希望每个着色器都有输入和输出,这样才能进行数据交流和传递。GLSL定义了inout关键字专门来实现这个目的。每个着色器使用这两个关键字设定输入和输出,只要一个输出变量与下一个着色器阶段的输入匹配,它就会传递下去。但在顶点和片段着色器中会有点不同。

      顶点着色器应该接收的是一种特殊形式的输入,否则就会效率低下。顶点着色器的输入特殊在,它从顶点数据中直接接收输入。为了定义顶点数据该如何管理,我们使用location这一元数据指定输入变量,这样我们才可以在CPU上配置顶点属性。我们已经在前面的教程看过这个了,layout (location = 0)。顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。

      你也可以忽略layout(location = 0)标识符,通过在OpenGL代码中使用glGetAttribLocation查询属性位置值(Location),但是我更喜欢在着色器中设置它们,这样会更容易理解而且节省你(和OpenGL)的工作量。

      另一个例外是片段着色器,它需要一个vec4颜色输出变量,因为片段着色器需要生成一个最终输出的颜色。如果你在片段着色器没有定义输出颜色,OpenGL会把你的物体渲染为黑色(或白色)。

    所以,如果我们打算从一个着色器向另一个着色器发送数据,我们必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了(这是在链接程序对象时完成的)。为了展示这是如何工作的,我们会稍微改动一下之前教程里的那个着色器,让顶点着色器为片段着色器决定颜色。

    输入与输出

      虽然着色器是各自独立的小程序,但是它们都是一个整体的一部分,出于这样的原因,我们希望每个着色器都有输入和输出,这样才能进行数据交流和传递。GLSL定义了inout关键字专门来实现这个目的。每个着色器使用这两个关键字设定输入和输出,只要一个输出变量与下一个着色器阶段的输入匹配,它就会传递下去。但在顶点和片段着色器中会有点不同。

      顶点着色器应该接收的是一种特殊形式的输入,否则就会效率低下。顶点着色器的输入特殊在,它从顶点数据中直接接收输入。为了定义顶点数据该如何管理,我们使用location这一元数据指定输入变量,这样我们才可以在CPU上配置顶点属性。我们已经在前面的教程看过这个了,layout (location = 0)。顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。

      你也可以忽略layout(location = 0)标识符,通过在OpenGL代码中使用glGetAttribLocation查询属性位置值(Location),但是我更喜欢在着色器中设置它们,这样会更容易理解而且节省你(和OpenGL)的工作量。

      另一个例外是片段着色器,它需要一个vec4颜色输出变量,因为片段着色器需要生成一个最终输出的颜色。如果你在片段着色器没有定义输出颜色,OpenGL会把你的物体渲染为黑色(或白色)。

    所以,如果我们打算从一个着色器向另一个着色器发送数据,我们必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了(这是在链接程序对象时完成的)。为了展示这是如何工作的,我们会稍微改动一下之前教程里的那个着色器,让顶点着色器为片段着色器决定颜色。

    顶点着色器

    片段着色器

    可以看到我们在顶点着色器中声明了一个vertexColor变量作为vec4输出,并在片段着色器中声明了一个类似的vertexColor。由于它们名字相同且类型相同,片段着色器中的vertexColor就和顶点着色器中的vertexColor链接了。由于我们在顶点着色器中将颜色设置为深红色,最终的片段也是深红色的。下面的图片展示了输出结果:

    完成了!我们成功地从顶点着色器向片段着色器发送数据。让我们更上一层楼,看看能否从应用程序中直接给片段着色器发送一个颜色!

    Uniform

    Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

    我们可以在一个着色器中添加uniform关键字至类型和变量名前来声明一个GLSL的uniform。从此处开始我们就可以在着色器中使用新声明的uniform了。我们来看看这次是否能通过uniform设置三角形的颜色:

    我们在片段着色器中声明了一个uniform vec4的ourColor,并把片段着色器的输出颜色设置为uniform值的内容。因为uniform是全局变量,我们可以在任何着色器中定义它们,而无需通过顶点着色器作为中介。顶点着色器中不需要这个uniform,所以我们不用在那里定义它。

    如果你声明了一个uniform却在GLSL代码中没用过,编译器会静默移除这个变量,导致最后编译出的版本中并不会包含它,这可能导致几个非常麻烦的错误,记住这点!

    这个uniform现在还是空的;我们还没有给它添加任何数据,所以下面我们就做这件事。我们首先需要找到着色器中uniform属性的索引/位置值。当我们得到uniform的索引/位置值后,我们就可以更新它的值了。这次我们不去给像素传递单独一个颜色,而是让它随着时间改变颜色:

    首先我们通过glfwGetTime()获取运行的秒数。然后我们使用sin函数让颜色在0.0到1.0之间改变,最后将结果储存到greenValue里。

    接着,我们用glGetUniformLocation查询uniform ourColor的位置值。我们为查询函数提供着色器程序和uniform的名字(这是我们希望获得的位置值的来源)。如果glGetUniformLocation返回-1就代表没有找到这个位置值。最后,我们可以通过glUniform4f函数设置uniform值。注意,查询uniform地址不要求你之前使用过着色器程序,但是更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的。

    因为OpenGL在其核心是一个C库,所以它不支持类型重载,在函数参数不同的时候就要为其定义新的函数;glUniform就是一个典型的例子。这个函数有一个特定的后缀,标识设定的uniform的类型,可能的后缀有:

    每当你打算配置一个OpenGL的选项的时候,就可以简单地根据这些规则选择适合你的数据类型的重载函数。在我们的例子里,我们希望分别设定uniform的4个float值,所以我们通过glUniform4f传递我们的数据(注意,我们也可以使用fv版本)。

    现在你知道如何设置uniform变量的值了,我们可以使用它们来渲染了。如果我们打算让颜色慢慢变化,我们就要在游戏循环的每一次迭代中(所以他会逐帧改变)更新这个uniform,否则三角形就不会改变颜色。下面我们就计算greenValue然后每个渲染迭代都更新这个uniform:

    这里的代码对之前代码是一次非常直接的修改。这次,我们在每次迭代绘制三角形前先更新uniform值。如果你正确更新了uniform,你会看到你的三角形逐渐由绿变黑再变回绿色。

    可以看到,uniform对于设置一个在渲染迭代中会改变的属性是一个非常有用的工具,它也是一个在程序和着色器间数据交互的很好工具,但假如我们打算为每个顶点设置一个颜色的时候该怎么办?这种情况下,我们就不得不声明和顶点数目一样多的uniform了。在这一问题上更好的解决方案是在顶点属性中包含更多的数据,这是我们接下来要做的事情。

    更多属性

    在前面的教程中,我们了解了如何填充VBO、配置顶点属性指针以及如何把它们都储存到一个VAO里。这次,我们同样打算把颜色数据加进顶点数据中。我们将把颜色数据添加为3个float值至vertices数组。我们将把三角形的三个角分别指定为红色、绿色和蓝色:

    由于现在有更多的数据要发送到顶点着色器,我们有必要去调整一下顶点着色器,使它能够接收颜色值作为一个顶点属性输入。需要注意的是我们用layout标识符来把aColor属性的位置值设置为1:

    由于我们不再使用uniform来传递片段的颜色了,现在使用ourColor输出变量,我们必须再修改一下片段着色器:

    因为我们添加了另一个顶点属性,并且更新了VBO的内存,我们就必须重新配置顶点属性指针。更新后的VBO内存中的数据现在看起来像这样:

    知道了现在使用的布局,我们就可以使用glVertexAttribPointer函数更新顶点格式,

    glVertexAttribPointer函数的前几个参数比较明了。这次我们配置属性位置值为1的顶点属性。颜色值有3个float那么大,我们不去标准化这些值。

    由于我们现在有了两个顶点属性,我们不得不重新计算步长值。为获得数据队列中下一个属性值(比如位置向量的下个x分量)我们必须向右移动6个float,其中3个是位置值,另外3个是颜色值。这使我们的步长值为6乘以float的字节数(=24字节)。

    同样,这次我们必须指定一个偏移量。对于每个顶点来说,位置顶点属性在前,所以它的偏移量是0。颜色属性紧随位置数据之后,所以偏移量就是3 * sizeof(float),用字节来计算就是12字节。

    运行程序你应该会看到如下结果:

    这个图片可能不是你所期望的那种,因为我们只提供了3个颜色,而不是我们现在看到的大调色板。这是在片段着色器中进行的所谓片段插值(Fragment Interpolation)的结果。当渲染一个三角形时,光栅化(Rasterization)阶段通常会造成比原指定顶点更多的片段。光栅会根据每个片段在三角形形状上所处相对位置决定这些片段的位置。
    基于这些位置,它会插值(Interpolate)所有片段着色器的输入变量。比如说,我们有一个线段,上面的端点是绿色的,下面的端点是蓝色的。如果一个片段着色器在线段的70%的位置运行,它的颜色输入属性就会是一个绿色和蓝色的线性结合;更精确地说就是30%蓝 + 70%绿。

    这正是在这个三角形中发生了什么。我们有3个顶点,和相应的3个颜色,从这个三角形的像素来看它可能包含50000左右的片段,片段着色器为这些像素进行插值颜色。如果你仔细看这些颜色就应该能明白了:红首先变成到紫再变为蓝色。片段插值会被应用到片段着色器的所有输入属性上。

    展开全文
  • 在顶点和片段着色器之间有一个可选的几何着色器(Geometry Shader),几何着色器的输入是一个图元(如点或三角形)的一组顶点。几何着色器可以在顶点发送到下一着色器阶段之前对它们随意变换。然而,几何着色器最有趣...
  • 我们已经在前面教程中接触过其中的两个了:顶点着色器的输出向量gl_Position,和片段着色器的gl_FragCoord。 顶点着色器变量 List item我们已经见过gl_Position了,它是顶点着色器的裁剪空间输出位置向量。如果你...
  • cc3.x(cocos creator3.x)的着色器demo有点少,而且讲的不是很清晰,我这种业余自学小白学的真的很艰难,不过好赖算是啃的差不多了,所以有了这则小记,权当备忘录了。 首先顶点着色器,上一段代码: CCProgram vs ...
  • OpenGL计算着色器

    2021-03-22 09:22:11
    OpenGL计算着色器先上图,再解答。完整主要的源代码源代码剖析 先上图,再解答。 完整主要的源代码 /** * OpenGL 4 - Example 21 * * @author Norbert Nopper norbert@nopper.tv * * Homepage: ...
  • OpenGL几何着色器

    2021-03-20 14:04:57
    OpenGL几何着色器OpenGL几何着色器简介源代码剖析主要源代码 OpenGL几何着色器简介 我们早就开始使用顶点和切片着色器,但实际上我们错过了一种类型,称为几何着色器(GS)。这种类型的着色器是由微软在DirectX10中...
  • 如何把顶点数据通过应用程序发送到着色器程序 特殊输出变量 在讲解内容之前,先看一张图 GLSL 顶点着色器的输入和输入变量 先讲讲这个图!箭头的方向表示输入和输出 uniform 变量,程序中保持常量 ...
  • OpenGL之着色器

    2021-07-18 18:05:22
    着色器着色器语言(GLSL)基本变量类型向量中的成员选择结构体类型数组构造函数修饰符运算符函数顶点着色器(Vertex Shader)内置变量片段着色器(Fragment Shader)内置变量几何着色器 着色器语言(GLSL)   着色...
  • openGL着色器 (shader)

    2021-05-20 17:26:01
    着色器 (shader) 着色器(Shader)就是运行在GPU上的小程序。这些小程序在图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立的程序,因为它们之间...
  • 3 CPU处理 ---- 应用阶段 3.1 把数据加载到显存 3.1.1 加载模型数据例子 3.1.2 加载纹理的例子 3.2 设置渲染状态 3.3 调用Draw Call 3.4 小结 4 GPU处理 ----几何阶段 4.1 顶点数据是什么 4.2 顶点着色器 4.3 坐标...
  • OpenGL学习-着色器

    2021-09-02 15:25:47
    OpenGL学习-着色器 开发环境搭建参考:https://lexiaoyuan.blog.csdn.net/article/details/120059213 通用配置步骤 Visual Studio,创建一个新的空项目 复制glad.c文件到你的工程中 【新增】在...
  • opengl从入门到菜鸡工程师之路基础架构介绍顶点着色器、片段着色器顶点输入顶点着色器片段着色器把上面过程串起来数据输入纹理的使用 基础架构介绍 首先opengl是一个做显示的的东西。首先对他的一个基本流程有一个...
  • OpenGL着色器GLSL

    2021-03-19 14:52:25
    OpenGL着色器OpenGL着色器简介GLSL数据类型向量输入与输出顶点着色器片段着色器Uniform更多属性我们自己的着色器类从文件读取 OpenGL着色器简介 着色器(Shader)是运行在GPU上的小程序。这些小程序为图形渲染管线的...
  • varying 变量: 从顶点着色器向片元着色器中传入数据。 varying 变量只能是float(以及相关的vec2,vec3,vec4,mat2,mat3,mat4)。 前面我们了解到顶点着色器是控制的几何图形的位置、尺寸甚至指定几何图形的颜色...
  • blender 着色器节点

    2021-09-12 16:52:30
    Blender 程序化纹理入门 着色器节点详解汇总_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1hr4y1P7cN?p=1 快速连接两个 node:alt+鼠标右键,连接两个 node,出现红线,可以快速连接(第一个节点);...
  • // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' ... #pragma vertex vert//告诉Unity哪个函数包含了顶点着色器的代码,哪个函数包含了片元着色器的代码 #pragma fragment frag //使用一个结构体来定.
  • 顶点着色器入门(1)

    2021-03-11 10:34:05
    顶点着色器(vertex shader)是一个在图形卡的GPU上执行的程序,它替换了固定功能管线(fixedfunction pipeline)中的变换(transformation)和光照(lighting)阶段。(这不是百分之百的正确,因为顶点着色器可以被Direct3D...
  • OpenGL顶点着色器和片段着色器顶点着色器顶点着色器的结构顶点着色器的输入输出顶点着色器的输入顶点着色器的输出片段着色器片段着色器的结构片段着色器的输入输出片段着色器的输入片段着色器的输出 顶点着色器 顶点...
  • 如果我们打算从一个着色器向另一个着色器发送数据,我们必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送...
  • OpenGL使用计算着色器进行GPU光线跟踪先上图,再解答。完整主要的源代码源代码剖析 先上图,再解答。 完整主要的源代码 /** * OpenGL 4 - Example 30 * * @author Norbert Nopper norbert@nopper.tv * * ...
  • 1.什么是几何着色器 最近一直在练习写Shader,发现用几何着色器可以写出特别酷炫的效果。于是去学习了一下,这里做个总结以备查阅。 几何着色器位于渲染流水线的几何阶段,在顶点着色器与片元着色器之间。 备注:...
  • 关键词:着色器、顶点/片元着色器着色器类、绘制彩色三角形、索引缓冲对象(EBO)、VBO、VAO 与 EBO 之间的联系与区别、Uniform、绘制变色正方形
  • 再往后,顶点数据送到一个名为曲面细分顶点计算着色器(TES)的程序中,同时送到这个着色器的还有细分后的网格相对位置,这个相对位置跟细分方式相关。细分方式则由TES指定。TES生成网格点对应的顶点的位置以及其他...
  • OpenGl L3着色器

    2021-10-13 09:54:00
    着色器 着色器又叫Shader,是一种运行在GPU上的小程序。为图形渲染管线的某个特定部分运行。 着色器作用是处理输入并转换为输出。各个着色器之间不能相互影响,之间通过输入和输出进行沟通。 着色器对应的语言为 ...
  • OpenGL着色器

    2021-03-20 10:33:15
    OpenGL着色器OpenGL着色器简介源代码剖析主要源代码 OpenGL着色器简介 从本课开始,我们将学习的所有效果和技术都将通过着色器实现。着色器是创建 3D 图形的现代方法。从某种意义上说,这是一个后退,因为大多数 3D ...
  • unity计算着色器入门

    2021-05-18 14:56:56
    计算着色器(Compute Shader简称CS)的用途就是并行计算,适用于算法简单但数量庞大的算法 应用场景:液体模拟,布料解算,聚落模拟等。 下面尝试做一个CS的demo 之前在yt上看到一个CS的应用,效果非常帅 使用类似...
  • 文章目录前言绘制一个点初始化着色器存储限定符向 webgl 系统传递变量 前言 接前面文章,我们实现了一个最简单的 webgl 程序,但是只是程序运行起来只是漆黑一片,我们想要通过 webgl 绘制一些图形。这篇文章让我们...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 70,760
精华内容 28,304
关键字:

着色器

友情链接: matlab大作业.zip