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。为了展示一个完整的 sin 周期,可以对 sin 函数进行一些缩放和平移的操作,使其周期 T = 1.0:

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

同时,也需要将屏幕的像素坐标规范化,使其落在 0.0 到 1.0 之间。下面 st 的计算是一个很常用的操作,其中,gl_FragCoord 是像素的坐标,u_resolution 是画布的尺寸,两者相除可以将像素坐标规范化。

vec2 st = gl_FragCoord.xy / u_resolution;

最终代码:

#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;
}

最后得到的效果如下:

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

这非常好理解,颜色 gl_FragColor 的每个分量就是根据函数的值来构造的。

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 的基本功。理解并不断地练习如何使用这些函数,是非常重要的。

Shader 001 - 函数造型能力的更多相关文章

  1. Book of Shaders 01 - 关于函数造型能力的理解

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

  2. unity shader 常用函数列表

    此篇博客转自csdn的一位大牛. 中间排版出了一些问题 Intrinsic Functions (DirectX HLSL) The following table lists the intrins ...

  3. Shader的函数公式以及使用的场景

    Shader 是干什么的? 它的作用可以先简单理解为对屏幕上的物体,进行颜色处理. 而大家都知道,所有计算机,处理颜色的当然是显卡,也就是 GPU . 所以我们写 Shader 的目的就是告诉 GPU ...

  4. Unity3D -- shader常用函数和变量

    最近在学习Unity Shader,写Shader的时候总是忘记Unity为我们提供的函数.变量怎么写的,这里整理一下,方便自己查阅,也提供给网友,学习Shader不易. 1.函数 float3 Wo ...

  5. 渲染路径-surface shader 光照函数与渲染路径

    https://docs.unity3d.com/Manual/SL-SurfaceShaderLighting.html Lighting Model declaration Lighting mo ...

  6. CG Shader常用函数

    为了方便自己记忆,将常用的CG函数写于此 转载于 http://www.cppblog.com/lai3d/archive/2008/10/23/64889.html Name Syntax Desc ...

  7. Unity Shader常用函数,标签,指令,宏总结(持续更新)

    极端常用: UnityObjectToClipPos(v.vertex); 最基本的顶点变换,模型空间 ==>裁剪空间 mul(unity_ObjectToWorld, v.vertex); 顶 ...

  8. Unity3D -- shader光照常用函数和变量

    上一篇记录了shader常用函数和变量,这篇记录一些光照计算时常用函数和变量 1.内置的光照变量 _LightColor0 float4 //该Pass处理的逐像素光源的颜色 _WorldSpaceL ...

  9. 匹夫细说C#:从园友留言到动手实现C#虚函数机制

    前言 上一篇文章匹夫通过CIL代码简析了一下C#函数调用的话题.虽然点击进来的童鞋并不如匹夫预料的那么多,但也还是有一些挺有质量的来自园友的回复.这不,就有一个园友提出了这样一个代码,这段代码如果被编 ...

随机推荐

  1. IDEA 非常重要的一些设置项 → 一连串的问题差点让我重新用回 Eclipse !

    开心一刻 建筑行业内,我看过的最凶残笑话(IT行业内好一致!) 上联:一天晚上两个甲方三更半夜四处催图只好周五加班到周六早上七点画好八点传完九点上床睡觉十分痛苦 下联:十点才过九分甲方八个短信七个电话 ...

  2. [netty4][netty-common]netty之ResourceLeakDetector的使用与实现

    netty之ResourceLeakDetector的使用与实现 通过WeakReference和ReferenceQueue做针对需要手动释放的资源的侦测 使用 设置日志级别: ServerBoot ...

  3. C++ U型数

    U型数字 最近蒜头君喜欢上了U型数字,所谓U型数字,就是这个数字的每一位先严格单调递减,后严格单调递增.比如 212212 就是一个U型数字,但是 333333, 9898, 567567, 3131 ...

  4. 尾递归(java)

    一般递归: 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多 ...

  5. neutron plugin 与 extension 编写流程

    原文链接:neutron plugin 与 extension 编写流程 参考: 怎样写 OpenStack Neutron 的 Plugin (一)怎样写 OpenStack Neutron 的 P ...

  6. pandas 数据类型转换及描述统计

    处理数据的时候往往需要对原始数据进行类型转换和预览等操作,下面介绍常用的处理预览和数据转换方法 预览:例: import pandas as pdsec_weather = pd.read_table ...

  7. mr原理简单分析

    背景 又是一个周末一天一天的过的好快,今天的任务干啥呢,索引总结一些mr吧,因为前两天有面试问过我?我当时也是简单说了一下,毕竟现在写mr程序的应该很少很少了,废话不说了,结合官网和自己理解写起. 官 ...

  8. Asp.net Core启动流程讲解(四)

    Asp.net Core内 DI(DependencyInjection)贯穿了项目的始终,要学习Asp.net Core就无法越过DI. 下面讲解一下DI在Asp.Net Core内的流程 asp. ...

  9. 使用tess4j完成身份证和营业执照图片的文字识别

    这两天研究了一下关于OCR图文解析的技术.当然市场上已经有开源服务,比如百度的AI开放平台,就有OCR相关的API接口.我这里选用的是Tesseract开源框架,java封装版本是tess4j.结合网 ...

  10. 初识ABP vNext(7):vue身份认证管理&租户管理

    Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 按钮级权限 身份认证管理 R/U权限 权限刷新 租户管理 租户切换 效果 最后 前言 上一篇介绍了vue+ABP国际化 ...