从顶点着色器到片元着色器

本文正在参加「金石计划」

简述

众所周知, 从顶点到片元经历了图元装配 、光栅化这两个主要流程。

图元装配又分图片组装和图元处理。

组装就是,按绘制模式,将顶点结合成完整的图元。 点模式,不用组装,一个点一个图元。 线模式,两个顶点组成一条线段图元。面模式, 三个顶点组成一个三角形图元。

图元处理的主要工作就裁剪,这个裁剪是指消除不在裁剪空间里的片元。 理点图元只需要简单的决定是否剔除这个点即可。 对于线面来说, 裁剪之后可能需要额外增加顶点

从顶点着色器到片元着色器

光栅化,就是把这些个图元离散化成一个个的栅格。 图形看上去是连续的,但是实际上我们的显示设备是离散的,所以必须处理成一个个的片元(对应一个像素)。

从顶点着色器到片元着色器

言归正传,本文要说的是顶点着色器到片元着色器, 写代码相关的。

顶点着色器

#version 300 es 
#define  attribute in
#define  varying  out 
// layout(location = 0)in  vec4 a_Position ; 
attribute vec4 a_Position ;
// layout(location = 1)in  vec3  a_Color ; 
attribute vec3 a_Color ;
attribute vec2 a_Uv;
varying highp vec2 v_Uv;
varying vec3 v_Color ;

// 3.0 没有 attribute varying  但是可以通过预编译指令兼容
void main(){
    gl_Position = a_Position ;
    gl_PointSize = 2.0;
    v_Uv = a_Uv;
    v_Color= a_Color;
}

我们使用JS往着色器传输数据(也可以说是从CPU到GPU),有两种, attributeuniform

之所以这么叫法,是GLSL1.0的规范,就是这两个关键字。 而3.0版本后attribute改成了in 。 语义上更明确了,attribute,就是输入顶点着色器的数据。

顶点着色器的代码,在一次流水线中, 每一个顶点都会执行一次。 什么叫每个顶点都会执行一次,就是如果把整个的顶点着色器代码,看做是一个函数, 这些个用attribute或者in修饰的变量,其实就是函数的参数 ,遍历每一个顶点把对应的参数传进去,执行整个着色器代码。

上面的片元着色器中,输入了a_Position, a_Uv, v_Color这三个attribute变量,每次执行顶点着色器代码的时候,这个三个变量就会是对应的值。

varying在GLSL1.0中表示 顶点着色器输出到片元着色器的数据,3.0改为out关键字。 结合起来看就是一个输入in,一个输出out

gl_Position是内置的输出变量,所以不需要用关键字。 它决定了顶点的位置,也就是后面在哪一块绘图。

小结一下就是,顶点着色器输入attribute(in)变量,输入varying(out)变量 和内置gl_Position,uniform 只有输入。

片元着色器

#version 300 es 

precision  mediump float;
#define  varying  in 

out highp vec4 Ocolor ;
#define gl_FragColor Ocolor
varying highp vec2 v_Uv;
in vec3 v_Color ;
uniform vec2 u_CanvasSize ;
uniform vec2 u_Mouse ;
uniform float u_Time ;
// 3.0 没有 attribute varying  但是可以通过预编译指令兼容

void main(){
    t = u_Time/1000.;
    vec2  uv =gl_FragColor.xy/ u_CanvasSize;

    gl_FragColor = vec4(v_Color,1.);
}

这里,单讲面模式。 面模式下的片元,就是开头所说的光栅化得到的。 片元着色器的代码,每个片元都会执行一次。所以,片元着色器的执行次数可想而知,一般都建议把能放在顶点着色器的计算,就不要拿到片元来了。

对于片元着色器来说 , gl_FragCoord 等于是内置的输入变量,四维向量,其xy对应的是物理坐标,如果是webgl,那就是对应画布元素上点坐标,以画布的左下角为原点,Y轴向上,X轴向右。z是深度,值域[0,1],w对应齐次坐标的w。 w的用途一般就是去齐次。

可以看做是,gl_FragCoord接收了顶点着色器输出的gl_Position, 当然,是插值后的。

线性插值

上面的代码里,输入了v_Uv v_Color这两个变量 , 这两个变量哪里来的? 就是前面的顶点着色器输出的, 并且经过插值了,才输入到片元这里。 插值的方式当然是线性插值。

关于插值的理解,请看绘制的结果。 我们只是分别给了四个点四种颜色, 结果可以看到,在四个角上,确实是那四种颜色,但是中间区域却是他们的混合。

假如两个顶点AB的颜色分别是a b, 那么 a ,b 连线上任一点的颜色为 color = x * a + (1-x)* b。 其中 x 代表的是在线段AB上的百分比。

从顶点着色器到片元着色器

正常情况下是插值的,我们再来看一下,不插值的效果。 flat 关键字可以实现这一点,不插值的情况下,一般都是取这个图元的最后一个顶点对应的数据。我这里用的是三角扇模式, 第二个三角形的最后一个顶点就是右下角那个,而第一个三角形的最后一个顶点是左下角那个。

flat out vec3 v_Color ;
..........
flat in vec3 v_Color ;

从顶点着色器到片元着色器

现在,想必对于片元的插值有了更感性的认知了吧。

有了输入,那么输出什么呢? 输出颜色,每个片元会对应一个颜色输出,片元着色器结束之后,对应片元的颜色就确定了。 GLSL1.0 用的是内置变量 gl_FragColor来接收要输出的颜色, 到了3.0之后,没有这个内置变量了, 需要自己声明一个, 高精度的四维向量,用作颜色输出。

简单理解,就是顶点着色器定形,片元着色器上色。

shaderToy的代码,形已经完全定了,就是一个矩形,剩余的全靠片元着色器发挥。

小结一下就是, 片元着色器能接收从顶点着色器输出的varying(out)变量,并最终输出颜色变量。

补充和结束

补充一下uniform的例子, 这里就是给所有的顶点加一个相同的偏移量,每个顶点都在x上偏移了.2,结果就是整个图形往右移动了一点,所以也可以称之为整体控制的变量,uniform 影响的是整个图片。

//js
   gl.uniform1f(offset,    
.2);
  // 顶点着色器
uniform float offset ;
。。。
gl_Position.x+=offset ;
   

从顶点着色器到片元着色器

最后用一份伪代码结束。

从顶点着色器到片元着色器

原文链接:https://juejin.cn/post/7214110277120507962 作者:莫石

(0)
上一篇 2023年3月25日 上午11:15
下一篇 2023年3月25日 上午11:35

相关推荐

发表评论

登录后才能评论