0x00 从函数出发

Shader 中的很多效果都是由函数计算得出的,如何更好地理解二者的关系呢。不妨先看看函数是什么?函数的定义可以简单地描述为:给定一个集合 A,对于其中的元素施加法则 f,则可以得到另一个集合 B。将这样的 A 和 B 中的元素的对应关系,反映到二维直角坐标系中,就可以得到一条关于 f 的曲线。比如,正弦函数 sin 的曲线。

那么,应当如何通过函数来得到想要的 Shader 效果?

我们都知道Shader 的中文翻译为着色器,Shader 的作用就是为屏幕中的每个像素着色。一段 Shader 程序的输入是位置信息,输出则是颜色信息。是不是很像函数中的映射关系:f(位置) = 颜色。

0x01 sin 的颜色

有了上面的表达式,稍加转变,我们就可以用 shader 来描述 sin 的颜色了。

在 shader 中,颜色是由一个四维浮点数向量 vec4 来表示的,分别代表 (r, g, b, a)。其中,每个量的取值区间都是 0.0 到 1.0。

[0, 1] 是一个很重要的区间,shader 中的许多操作都是在这个区间上完成的。比如,坐标的规范化,通过像素坐标 gl_FragCoord 与屏幕大小 u_resolution 相除,使得坐标落在 0.0 到 1.0 上。

vec2 st = gl_FragCoord.xy / u_resolution;

我们不妨用规范化后的横坐标来表示函数中的自变量,用计算出的 sin 为颜色赋值。在此之前,还需要对 sin 函数进行一些缩放和平移操作,使其周期 T = 1,振幅 2A = 1,最小值 = 0,最大值 = 1。这样在规范化的横坐标区间 [0, 1] 上就能展示一个完整的 sin 周期。同时,sin 的值域也刚好对应颜色的取值区间,也是 0 到 1。

float f = sin(x * PI_2) / 2.0 + 0.5;

最终代码:

#ifdef GL_ES
precision mediump float;
#endif #define PI_2 6.2831853 uniform vec2 u_resolution; // 绘制 y 和 x 对应关系的曲线
float plot(float y, float x) {
return smoothstep(x - 0.01, x, y) - smoothstep(x, x + 0.01, y);
} void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
float f = sin(st.x * PI_2) / 2.0 + 0.5;
vec4 color = vec4(f);
float p = plot(st.y, f);
color = (1.0 - p) * color + p * vec4(0.0, 1.0, 0.0, 1.0);
gl_FragColor = color;
}

最后得到的效果如下:

仔细观察从左到右的颜色变化,以及 sin 曲线的高度变化。不难发现,函数值越大的地方,颜色就越白,即,越接近白色的 rgba (1.0, 1.0, 1.0, 1.0);而函数值越小的地方,颜色就越黑,即,越接近黑色的 rgba (0.0, 0.0, 0.0, 0.0)。

这非常好理解,颜色 gl_FragColor 中的每个元素的值就是根据函数的值来构造的。这就是 "sin 的颜色"。

0x02 sin 的形状

函数不仅能表现色彩,还能表现形状和动态。不妨再观察一下上面效果图中 sin 的曲线变化,是不是很像一座座高低起伏的山呢?只需对上面的代码稍加改造,就能得到几座连绵的绿色小山。

#ifdef GL_ES
precision mediump float;
#endif #define PI_2 6.2831853 uniform vec2 u_resolution;
uniform float u_time; void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
// st.x += u_time / 2.0;
float f = sin(st.x * PI_2 * 2.0) / 8.0 + 0.2;
float p = 1.0 - smoothstep(f, f + 0.01, st.y);
vec4 color = p * vec4(0.0, 1.0, 0.0, 1.0);
// color = p * vec4(0.1, 0.3, 0.4, 1.0);
gl_FragColor = color;
}

几座抽象的绿色小山,虽然看上去有点粗糙:

还可以取消上面代码中的注释,利用 u_time 值赋予画面一些动态。几座绿色的小山又变成波动的海浪。

0x04 理解练习掌握

本文仅谈论了最基本的 sin 函数,但也不难看出,sin 是一个强有力的造型工具。再结合另外两个函数工具:fract(取分数部分) 和 dot(点乘),我们还能用 sin 来绘制一幅简单的噪声图。

#ifdef GL_ES
precision mediump float;
#endif uniform vec2 u_resolution; float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
} void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
float rnd = random(st);
gl_FragColor = vec4(vec3(rnd),1.0);
}

噪声在图形学中有广泛的应用,比如,用来模拟一些不规则的动态表面:火焰、云、岩石等。

在 Shader 中需要时时刻刻与各种函数打交道,正是这些函数多样的造型能力以及它们之间的有机结合,实现了多种多样的 Shader 效果。

正确使用这些函数的能力,就是 Shader 的基本功。理解并不断地练习如何使用这些函数,是非常重要的。

参考资料:

Book of Shaders 01 - 关于函数造型能力的理解的更多相关文章

  1. Shader 001 - 函数造型能力

    0x00 从函数出发 Shader 中的很多效果都是由函数计算得出的,如何更好地理解二者的关系呢.不妨先看看函数是什么?函数的定义可以简单地描述为:给定一个集合 A,对于其中的元素施加法则 f,则可以 ...

  2. javaScript系列 [01]-javaScript函数基础

    [01]-javaScript函数基础 1.1 函数的创建和结构 函数的定义:函数是JavaScript的基础模块单元,包含一组语句,用于代码复用.信息隐蔽和组合调用. 函数的创建:在javaScri ...

  3. 谈谈自己对C语言中函数指针的一些理解 (第一次写博客,有点小兴奋哈)

    1.函数指针声明的格式及简单的使用 (1)格式:(返回值)(*函数指针名)(参数列表)    例如:声明一个无参数无返回值的函数指针(void)(*p)(void). (2)将函数指针指向某个无参数无 ...

  4. setdefault函数的用法及理解

    setdefault函数的用法及理解 dict.setdefault(key, default=None) 功能:如果键不存在于字典中,将会添加该键并将default的值设为该键的默认值,如果键存在于 ...

  5. (python函数01)enumerate函数

    enumerate函数 enumerate函数示例代码01运行结果01示例代码02运行结果02 enumerate(枚举,列举)是python的内置函数,enumerate的参数为可迭代对象,如字符串 ...

  6. 0-C相关01:NSlog函数介绍。

      NSlog()函数介绍: 首先:NSlog()函数是cocoa的框架中提供的一个方法: 下图中最上方是它在Xcode中的路径: : 同样都是输出函数.下边我们来看一下,在O-C中NSlog()和在 ...

  7. Socket 学习笔记 01 常用函数

    常用方法 创建套接字: socket()    绑定本机端口: bind()    建立连接: connect(),accept()    侦听端口: listen()    数据传输: send() ...

  8. 提高你的Python能力:理解单元测试

    对于程序开发新手来说,一个最常见的困惑是测试的主题.他们隐约觉得“单元测试”是很好的,而且他们也应该做单元测试.但他们却不懂这个词的真正含义.如果这听起来像是在说你,不要怕!在这篇文章中,我将介绍什么 ...

  9. [置顶] 函数传递不定参数理解-c语言

    感性认识 Typedef char *va_list;/*这个在<stdatg.h>中有定义*/ #define va_start(ap,p) (ap=(char*)(&(p)+1 ...

随机推荐

  1. 架构设计 | 基于电商交易流程,图解TCC事务分段提交

    本文源码:GitHub·点这里 || GitEE·点这里 一.场景案例简介 1.场景描述 分布式事务在业务系统中是十分常见的,最经典的场景就是电商架构中的交易业务,如图: 客户端通过请求订单服务,执行 ...

  2. Element UI - DatePicker 自定义日期选择期间

    <el-date-picker v-else v-model="searchForm.data_Selected" type="daterange" un ...

  3. 如何自制WC3地形纹理贴图

    http://world-editor-tutorials.thehelper.net/tilesets.php https://wenku.baidu.com/view/e761c953cc1755 ...

  4. 白嫖码云Pages,两分钟的事,就能搭个百度能搜到的个人博客平台

    为了攒点钱让女儿做个富二代(笑),我就没掏钱买服务器,白嫖 GitHub Pages 搭了一个博客平台.不过遗憾的是,GitHub Pages 只能被谷歌收录,无法被百度收录,这就白白损失了一大波流量 ...

  5. JavaScript 时间都去那了(操作时间字符串加减时间)

    ---给时间充点时间吧--- 时间转换函数: function dateFormat(date, format) { date = new Date(date); var o = { 'M+': da ...

  6. Prometheus 与 国产 TDengine 的对比

    通过对比,能加深对这两个系统的理解,方便后续架构选型时作出正确决定.他们的设计思路有很多值得借鉴的地方,虽然工作中需要用到这些知识的地方不多,但是了解他们的设计细节能极大满足我的好奇心. 1.场景和需 ...

  7. 十分钟快速入门 Python

    本文以 Eric Matthes 的<Python编程:从入门到实践>为基础,以有一定其他语言经验的程序员视角,对书中内容提炼总结,化繁为简,将这本书的精髓融合成一篇10分钟能读完的文章. ...

  8. 实验 2:Mininet 实验——拓扑的命令脚本生成

    实验 2:Mininet 实验--拓扑的命令脚本生成 一.实验目的 掌握 Mininet 的自定义拓扑生成方法:命令行创建.Python 脚本编写 二.实验任务 通过使用命令行创建.Python 脚本 ...

  9. JVM调优和深入了解性能优化

    JVM调优的本质: 并不是显著的提高系统性能,不是说你调了,性能就能提升几倍或者上十倍,JVM调优,主要调的是稳定.如果你的系统出现了频繁的垃圾回收,这个时候系统是不稳定的,所以需要我们来进行JVM调 ...

  10. vps+v_2_ray+proxychains

    电脑系统换到Linux快半年了,之前一直没有解决的问题是怎么上google,毕竟有些东西还是google上好找一点.最近不想复习,没想到自己成功搭了个梯子,着实把惊喜了我一把.下面记录一下过程. 首先 ...