学习WebGL之深入了解Shader

本系列所有文章目录

本文将带大家深入了解Shader,下面是运行截图,可以前往我的博客查看代码演示。

前言

上篇文章中我们已经和Shader有了一面之缘,本文将带大家深入Shader的世界,介绍Shader的语言特性,数据类型,内置方法等等。Shader语言和C语言很相似,如果你学过C语言应该可以很快适应Shader的编程风格。本文提供了一个具备Shader基本编程元素的例子,通过Shader控制三角形旋转,并把位置转变成了颜色,读者可以通过修改这个例子更快的熟悉Shader。

代码框架

无论是Vertex Shader还是Fragment Shader,都有基本的代码框架。下面是本文使用的Vertex Shader。

attribute vec4 position;
varying vec4 fragColor;
uniform float elapsedTime;
void main() {
    fragColor = position * 0.5 + 0.5;
    float rotateAngle = elapsedTime * 0.001;
    float x = position.x * cos(rotateAngle) - position.y * sin(rotateAngle);
    float y = position.x * sin(rotateAngle) + position.y * cos(rotateAngle);
    gl_Position = vec4(x, y, 0.0, 1.0);
}

只有Vertex Shader可以声明attribute变量,它用来接受CPU传递过来的顶点数据。除了attribute变量之外,还可以声明uniform变量和varying变量。具体含义还以下面会作详解。main方法是Shader代码执行的入口,这和C语言一模一样。在Vertex Shader中你必须给gl_Position赋值,否则这个Vertex Shader没有任何意义,没有任何顶点会传递给GPU。

下面是本文使用的Fragment Shader。

varying mediump vec4 fragColor;
void main() {
    gl_FragColor = fragColor;
}

Fragment Shader除了attribute变量其他变量都可以拥有,varying变量必须和Vertex Shader中的varying变量类型保持一致,varying变量会从Vertex Shader传递到Fragment Shader中。那么问题来了,Vertex Shader是每个顶点调用一次,而Fragment Shader是每个像素调用一次,那么顶点之间像素的varying变量值是如何计算的呢?答案是GPU会根据要绘制的形状自动插值计算。大家可以观察示例,三角形顶点之间的颜色正是通过各个顶点的fragColor插值计算出来的。

变量类型

Shader有下面几种变量类型:

  • void 和C语言的void一样,无类型
  • bool 布尔
  • int 有符号的int
  • float 浮点数
  • vec2, vec3, vec4 2,3,4维向量,如果你不知道什么是向量,可以理解为2,3,4长度的数组。
  • bvec2, bvec3, bvec4 2,3,4维布尔值的向量。
  • ivec2, ivec3, ivec4 2,3,4维int值的向量。
  • mat2, mat3, mat4 2x2, 3x3, 4x4 浮点数的矩阵,如果你不了解矩阵,后面会有一篇文章单独介绍矩阵。
  • sampler2D 纹理,后面会详细介绍。
  • samplerCube Cube纹理,后面会详细介绍。

变量精度

细心的读者可能会发现同样是varying变量,在Fragment Shader中多了一个mediump修饰符。mediump表示的是变量类型的精度。因为Fragment Shader是逐像素执行,所以会尽量控制计算的复杂度。对于不需要过高精度的变量,可以手动指定精度从而提高性能。精度主要分为下面3种。

  • highp, 16bit,浮点数范围(-2^62, 2^62),整数范围(-2^16, 2^16)
  • mediump, 10bit,浮点数范围(-2^14, 2^14),整数范围(-2^10, 2^10)
  • lowp, 8bit,浮点数范围(-2, 2),整数范围(-2^8, 2^8)
    如果你想所有的float都是高精度的,可以在Shader顶部声明precision highp float;,这样你就不需要为每一个变量声明精度了。

运算符

Shader可以使用所有C语言的运算符。不过要注意的是二元运算比如加法,乘法等,只能用在两个类型相同的变量上,比如float只能和float相加。因为Shader不会为你进行隐式的类型转换,这样会增加GPU的负担。我们表示float变量时,需要自行增加小数点,比如浮点数5要写成5.0,否则会被认定为整数。下面是能够使用的运算符,读者可以当做参考。


uniform变量

uniform变量会被所有Shader共享,比如有3个顶点,Vertex Shader会被执行3次,每次访问的uniform变量都是同一个由js代码设定好的值。下面是本文使用的设定uniform elapsedTime的js代码。

  elapsedTimeUniformLoc = gl.getUniformLocation(program, 'elapsedTime');
  gl.uniform1f(elapsedTimeUniformLoc, elapesdTime);

首先获取uniform elapsedTime在Shader中的位置,然后设置它的值。uniform1funiformXXX函数簇里面用来设置一个float类型uniform的方法。通过uniformXXX里的XXX很容易看出来这个方法是设置什么类型的uniform的,下面是常见的几种格式。

  • uniform{n}{type} n表示数目1~4,type表示类型,floatfintiunsigned intui。所以设置一个整数就是 uniform1i
  • uniform{n}{type}v 相比于上面的多了一个v,表示向量,所以传递的参数就是类型为type,维度为n的向量。
  • uniformMatrix{n}{type}v 这个用来设置类型为type nxn的矩阵。
    上面这些方法会在后面的文章中用到,这里大致了解即可。

varying变量

varying变量是Vertex Shader和Fragment Shader的桥梁,Fragment Shader中的varying变量由Vertex Shader中的varying变量自动插值计算出来。因为Fragment Shader是逐像素执行,某些使用varying变量的效果在Fragment Shader中实现会更加细腻,比如光照效果。

向量的访问

当我们拥有一个vec4变量,我们可以有很多种方法访问它内部的值。

  • vec4.x,vec4.y,vec4.z,vec4.w 通过x,y,z,w可以分别取出4个值。
  • vec4.xy,vec4.xx,vec4.xz,vec4.xyz 任意组合可以取出多种维度的向量。
  • vec4.r,vec4.g,vec4.b,vec4.a 还可以通过r,g,b,a分别取出4个值,同上可以任意组合。
  • vec4.s,vec4.t,vec4.p,vec4.q 还可以通过s,t,p,q分别取出4个值,同上可以任意组合。
    vec3vec2也是同样,无非就是少了几个变量,比如vec3只有x,y,z。vec2只有x,y。

内置方法

有很多内置方法可以使用,如果可以选择内置方法实现算法,避免自己写代码再实现一遍,因为内置的方法能够得到更好的硬件支持。下面是可用的方法表格。



可能很多方法你都不知道有什么用,没关系,后面使用到时我会再做解释。

上面的介绍覆盖了Shader的大部分基础知识,当然还有很多使用细节和不常用的知识没有介绍,这些知识会在后面使用到时再详细介绍,这样可以避免大家刚开始就对Shader产生畏惧感。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容