cocos cretor shader effect-the book of shader-1.造型函数

2024-02-17 23:50

本文主要是介绍cocos cretor shader effect-the book of shader-1.造型函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Shaping functions(造型函数)

书本原链接:造型函数

这一章应该叫做宫城先生的粉刷课(来自电影龙威小子的经典桥段)。之前我们把规范化后的 x,y 坐标映射(map)到了红色和绿色通道。本质上说我们是建造了这样一个函数:输入一个二维向量(x,y),然后返回一个四维向量(r,g,b,a)。但在我们跨维度转换数据之前,我们先从更加…更加简单的开始。我们来建一个只有一维变量的函数。你花越多的时间和精力在这上面,你的 shader 功夫就越厉害。

(img-Gz7nmg4s-1612272112059)(img/3/3-1.jpg)]

接下来的代码结构就是我们的基本功。在它之中我们对规范化的 x 坐标(st.x)进行可视化。有两种途径:一种是用亮度(度量从黑色到白色的渐变过程),另一种是在顶层绘制一条绿色的线(在这种情况下 x 被直接赋值给 y)。不用过分在意绘制函数,我们马上会更加详细地解释它

#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;// Plot a line on Y using a value between 0.0-1.0
float plot(vec2 st) {    return smoothstep(0.02, 0.0, abs(st.y - st.x));
}void main() {vec2 st = gl_FragCoord.xy/u_resolution;float y = st.x;vec3 color = vec3(y);// Plot a linefloat pct = plot(st);color = (1.0-pct)*color+pct*vec3(0.0,1.0,0.0);gl_FragColor = vec4(color,1.0);
}

简注 :vec3 类型构造器“明白”你想要把一个值赋值到颜色的三个通道里,就像 vec4 明白你想要构建一个四维向量,三维向量加上第四个值(比如颜色的三个值加上透明度)。

这些代码就是你的基本功;遵守和理解它非常重要。你将会一遍又一遍地回到 0.0 到 1.0 这个区间。你将会掌握融合与构建这些代码的艺术。

在cocos上实现本书中的shader代码

PS:本教程所有cocosCreator版本是2.4.3

// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.  CCEffect %{techniques:- passes:- vert: vsfrag: fsblendState:targets:- blend: truerasterizerState:cullMode: noneproperties:texture: { value: white }alphaThreshold: { value: 0.5 }
}%CCProgram vs %{precision highp float;#include <cc-global>#include <cc-local>in vec3 a_position;in vec4 a_color;out vec4 v_color;#if USE_TEXTUREin vec2 a_uv0;out vec2 v_uv0;#endifvoid main () {vec4 pos = vec4(a_position, 1);#if CC_USE_MODELpos = cc_matViewProj * cc_matWorld * pos;#elsepos = cc_matViewProj * pos;#endif#if USE_TEXTUREv_uv0 = a_uv0;#endifv_color = a_color;gl_Position = pos;}
}%CCProgram fs %{precision highp float;#include <alpha-test>#include <texture>in vec4 v_color;#if USE_TEXTUREin vec2 v_uv0;uniform sampler2D texture;#endiffloat plot(vec2 st) {    return smoothstep(0.02, 0.0, abs(st.y - st.x));}void main () {vec4 o = vec4(1, 1, 1, 1);#if USE_TEXTURECCTexture(texture, v_uv0, o);#endifo *= v_color;ALPHA_TEST(o);float y = v_uv0.x;vec3 color = vec3(y);float pct = plot(v_uv0);color = (1.0-pct)*color+pct*vec3(0.0,1.0,0.0);gl_FragColor = vec4(color,1.0);}
}%

代码有点长,因为这里截取了全部shader代码。在这本书上,我们只针对片段着色器去开发,也就是说,我们只要关注frag的代码就行了,也就是说我们主要是针对于片段着色器的开发。

后面的例子如果在cocos的写法上差别比较大的话,我会罗列出来,否则就直接按照书本的看就好。

看上面的代码我们可以知道,cocos上的shader代码和书本的基本上是,就是在书本上的st(相对位移),我们在cocos上可以用v_uv0代替,这个变量表示的是从顶点着色器输出的输入到片段着色器的顶点坐标,一般你在cocos创建了一个effet脚本就会默认带有的了。

这些 x 与 y(或亮度)之间一对一的关系称作线性插值(linear interpolation)。(译者注:插值是离散函数逼近的重要方法,利用它可通过函数在有限个点处的取值状况,估算出函数在其他点处的近似值。因为对计算机来说,屏幕像素是离散的而不是连续的,计算机图形学常用插值来填充图像像素之间的空隙。)现在起我们可以用一些数学函数来改造这些代码行。比如说我们可以做一个求 x 的 5 次幂的曲线。

// Title: Expo#ifdef GL_ES
precision mediump float;
#endif#define PI 3.14159265359uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;float plot(vec2 st, float pct){return  smoothstep( pct-0.02, pct, st.y) -smoothstep( pct, pct+0.02, st.y);
}void main() {vec2 st = gl_FragCoord.xy/u_resolution;float y = pow(st.x,5.0);vec3 color = vec3(y);float pct = plot(st,y);color = (1.0-pct)*color+pct*vec3(0.0,1.0,0.0);gl_FragColor = vec4(color,1.0);
}

很有趣,对吧?试试把pow(st.x,5.0)的指数值改为不一样的值,比如:20.0,2.0,1.0,0.0,0.2 或 0.02。理解值和指数之间的关系非常重要。这些数学函数可以让你灵动地控制你的代码,就像是给数据做针灸一样。

pow() (求x的y次幂)是 GLSL 的一个原生函数,GLSL 有很多原生函数。大多数原生函数都是硬件加速的,也就是说如果你正确使用这些函数,你的代码就会跑得更快。

cocos creator上的实际效果

(img-6MwGS7o2-1612272112062)(img/3/demo-3-1.png)]

仔细看看,咦,发现好像又什么不对劲,按照指数函数来说,这图像好像倒过来了。正常来说应该得到的是这样的:

(img-HB1LRCVU-1612272112063)(img/3/demo-3-2.png)]

这个就是上一篇说的坐标系的问题的,具体可以去看下这里 TheBookOfShader开始–关于坐标系的问题。我们只要把*float y = pow(st.x,5.0);改为float y = 1.-pow(st.x,5.0);*就行了。后面的很多例子也都是倒过来的,有些我会在源码上校正过来。

换掉第 19 行的幂函数,试试看exp()(以自然常数e为底的指数函数),log()(对数函数) 和 sqrt()(平方根函数)。当你用 Pi 来玩的时候有些方程会变得更有趣。在第 5 行我定义了一个宏,使得每当程序调用 PI 的时候就用 3.14159265359 来替换它

PS:这里书本让我们试试exp和log函数的时候,我们直接float y = exp(st.x);/log(st.x)替换的话,看到的只能是黑乎乎的或者白乎乎的一片。为什么会这样呢,我们可以看下exp和log的图像

(img-JJ3c0DPB-1612272112065)(img/exp-map.png)]

(img-1Cr18e5B-1612272112072)(img/log-map.png)]

从图像我们可以制作,因为st.x的取值范围是【0-1】,所有得到的y就是exp大于1,而log是小于0.所有得到的颜色不是0(黑色)就是1(白色)。我们可以稍加改变一下,如下:

float y =abs(log(v_uv0.x));
y=1.-(exp(v_uv0.x-1.));

Step 和 Smoothstep

GLSL 还有一些独特的原生插值函数可以被硬件加速。

step(float edge, float x) 插值函数需要输入两个参数。第一个是极限或阈值,第二个是我们想要检测或通过的值。对任何小于阈值的值,返回 0.0,大于阈值,则返回 1.0。

试试看改变下述代码中第 20 行的值。

#ifdef GL_ES
precision mediump float;
#endif#define PI 3.14159265359uniform vec2 u_resolution;
uniform float u_time;float plot(vec2 st, float pct){return  smoothstep( pct-0.02, pct, st.y) -smoothstep( pct, pct+0.02, st.y);
}void main() {vec2 st = gl_FragCoord.xy/u_resolution;// Step will return 0.0 unless the value is over 0.5,// in that case it will return 1.0float y = step(0.5,st.x);vec3 color = vec3(y);float pct = plot(st,y);color = (1.0-pct)*color+pct*vec3(0.0,1.0,0.0);gl_FragColor = vec4(color,1.0);
}

另一个 GLSL 的特殊函数是 smoothstep(float edge0, float edge1, float x)。当给定一个范围的上下限和一个数值,这个函数会在已有的范围内给出插值。前两个参数规定转换的开始和结束点,第三个是给出一个值用来插值

也就是说,当x < edge0时,返回的值是0;当 x >edge1时,返回的值是1。当edge0 < x < edge1时,返回的值是

    float t;  t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);return t * t * (3.0 - 2.0 * t);

smoothstep例子:

#ifdef GL_ES
precision mediump float;
#endif#define PI 3.14159265359uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;float plot(vec2 st, float pct){return  smoothstep( pct-0.02, pct, st.y) -smoothstep( pct, pct+0.02, st.y);
}void main() {vec2 st = gl_FragCoord.xy/u_resolution;// Smooth interpolation between 0.1 and 0.9float y = smoothstep(0.1,0.9,st.x);vec3 color = vec3(y);float pct = plot(st,y);color = (1.0-pct)*color+pct*vec3(0.0,1.0,0.0);gl_FragColor = vec4(color,1.0);
}

(img-dROkQ4eC-1612272112074)(img/3/demo-3-5.png)]

在之前的例子中,注意第 12 行,我们用到 smoothstep 在 plot() 函数中画了一条绿色的线。这个函数会对给出的 x 轴上的每个值,在特定的 y 值处制造一个凹凸形变。如何做到呢?通过把两个 smoothstep() 连接到一起。来看看下面这个函数,用它替换上面的float y = smoothstep(0.1,0.9,st.x);,把它想成是一个垂直切割。背景看起来很像一条线,不是吗?

float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);

cocos上的效果(这个是倒过来的,你可以尝试着正一下,哈哈哈)

(img-oy3wSNJw-1612272112076)(img/3/demo-3-3.png)]

PS:smoothstep函数在shader代码上无处不在,建议可以多花点时间好好思考下这个函数的意义与作用

正弦和余弦函数
当你想用数学来制造动效,形态或去混合数值,sin 和 cos 就是你的最佳伙伴。

这两个基础的三角函数是构造圆的极佳工具,就像张小泉的剪刀一样称手。很重要的一点是你需要知道它们是如何运转的,还有如何把它们结合起来。简单来说,当我们给出一个角度(这里采用弧度制),它就会返回半径为一的圆上一个点的 x 坐标(cos)和 y 坐标(sin)。正因为 sin 和 cos 返回的是规范化的值(即值域在 -1 和 1 之间),且如此流畅,这就使得它成为一个极其强大的工具。

(img-G3AOdgOM-1612272112078)(img/3/3-2.gif)]

尽管描述三角函数和圆的关系是一件蛮困难的事情,上图动画很棒地做到了这一点,视觉化展现了它们之间的关系。

(img-oVmXq6k6-1612272112078)(img/3/3-3.png)]

仔细看 sin 曲线。观察 y 值是如何平滑地在 +1 和 -1 之间变化。就像之前章节关于的 time 的例子中,你可以用 sin() 的有节奏的变动给其他东西加动效。这个会在后面的章节有涉及到,敬请期待。

  • 在 sin 里让 x 加上时间(u_time)。让sin 曲线随 x 轴动起来。
  • 在 sin 里用 PI 乘以 x。注意 sin 曲线上下波动的两部分如何收缩了,现在 sin 曲线每两个整数循环一次。
  • 在 sin 里用时间( u_time)乘以 x。观察各阶段的循环如何变得越来越频繁。注意 u_time 可能已经变得非常大,使得图像难以辨认。
  • 给 sin(x)(注意不是 sin 里的 x)加 1.0。观察曲线是如何向上移动的,现在值域变成了 0.0 到 2.0。
  • 给 sin(x) 乘以 2.0。观察曲线大小如何增大两倍。
  • 计算 sin(x) 的绝对值(abs())。现在它看起来就像一个弹力球的轨迹。
  • 只选取 sin(x) 的小数部分(fract())。
  • 使用向正无穷取整(ceil())和向负无穷取整(floor()),使得 sin 曲线变成只有 1 和 -1 的电子波。、

上面是书本上简单的几个test,大家可以试验下,主要改的还是下面这句话

y=2.*abs(sin(2.3.14v_uv0.x+2.*cc_time.x));

最后书本上介绍了一个很炫酷的效果,我在源码上也试着实现了一下,不过效果好像不是很好。

(img-oE8u6XfK-1612272112079)(img/3/demo-3-4.png)]

等写完前面4章后,我还会给大家放出我自己写的一个试验demo,让大家更加直观的感受到shader的魅力

(img-JfZ2R9ZZ-1612272112080)(img/demo.gif)]

完整源码

// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.  CCEffect %{techniques:- passes:- vert: vsfrag: fsblendState:targets:- blend: truerasterizerState:cullMode: noneproperties:texture: { value: white }alphaThreshold: { value: 0.5 }shapingType: {value: 1.0 }
}%CCProgram vs %{precision highp float;#include <cc-global>#include <cc-local>in vec3 a_position;in vec4 a_color;out vec4 v_color;#if USE_TEXTUREin vec2 a_uv0;out vec2 v_uv0;#endifvoid main () {vec4 pos = vec4(a_position, 1);#if CC_USE_MODELpos = cc_matViewProj * cc_matWorld * pos;#elsepos = cc_matViewProj * pos;#endif#if USE_TEXTUREv_uv0 = a_uv0;#endifv_color = a_color;gl_Position = pos;}
}%CCProgram fs %{precision highp float;#include <alpha-test>#include <texture>#include <cc-global>#include <cc-local>in vec4 v_color;#if USE_TEXTUREin vec2 v_uv0;uniform sampler2D texture;#endifuniform shapingValue{float shapingType;};vec3 colorA = vec3(0.149,0.141,0.912);vec3 colorB = vec3(1.000,0.833,0.224);float cubicPulse( float c, float w, float x ){x = abs(x - c);if( x>w ) return 0.0;x /= w;return 1.0 - x*x*(3.0-2.0*x);}float plot(vec2 st){return smoothstep(0.02,0.0,abs(st.y-st.x));}float plot2(vec2 st,float pct){return smoothstep(pct-.02,pct,st.y)-smoothstep(pct,pct+.02,st.y);}float smoothstep_plus(float v1,float v2,float v3,float v4,float t){return smoothstep(v1,v2,t)-smoothstep(v3,v4,t);}float smoothstep_train(float v1,float v2,float v3,float v4,float period,float t){return smoothstep_plus(v1,v2,v3,v4,mod(t,period));}float expImpulse(float x,float k){float h=k*x;return h*exp(1.-h);}//这里的shapingType是根据书本里不同的公司选择不同的效果,你可以在cocos编译器上输入123去查看不同公式对于的效果vec3 Line(vec2 st){float y=st.x;if(shapingType==2.) y=smoothstep(0.1,0.9,st.x);if(shapingType==3.) y=cubicPulse(.5,.2,st.x);vec3 color=vec3(y);float pct=plot(st);if(shapingType==1.) pct=plot(st);if(shapingType==2. || shapingType==3.) pct=plot2(st,y);color=(1.-pct)*color+pct*vec3(0.,1.,0.);return color;}void main () {vec4 o = vec4(1, 1, 1, 1);#if USE_TEXTURECCTexture(texture, v_uv0, o);#endifo *= v_color;vec3 _color=vec3(1.);ALPHA_TEST(o);_color=Line(v_uv0);vec3 c=vec3(0.);  float l,z=cc_time.x;  //书本上介绍的一个很炫酷的效果if(shapingType==4.){for(int i=0;i<3;i++){vec2 p=v_uv0;vec2 uv=p;p-=vec2(0.5);p.x*=v_uv0.x/v_uv0.y;z+=.07;l=length(p);uv+=p/l*(sin(z)+1.)*abs(sin(l*9.-z*2.));c[i]=.01/length(abs(mod(uv,1.)-.5));} gl_FragColor=vec4(c/l,cc_time.x);}else{gl_FragColor =vec4(_color,1.0);}//gl_FragCoord// gl_FragColor =vec4(_color,1.0);}
}%

这篇关于cocos cretor shader effect-the book of shader-1.造型函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/719373

相关文章

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

kotlin的函数forEach示例详解

《kotlin的函数forEach示例详解》在Kotlin中,forEach是一个高阶函数,用于遍历集合中的每个元素并对其执行指定的操作,它的核心特点是简洁、函数式,适用于需要遍历集合且无需返回值的场... 目录一、基本用法1️⃣ 遍历集合2️⃣ 遍历数组3️⃣ 遍历 Map二、与 for 循环的区别三、高

C语言字符函数和字符串函数示例详解

《C语言字符函数和字符串函数示例详解》本文详细介绍了C语言中字符分类函数、字符转换函数及字符串操作函数的使用方法,并通过示例代码展示了如何实现这些功能,通过这些内容,读者可以深入理解并掌握C语言中的字... 目录一、字符分类函数二、字符转换函数三、strlen的使用和模拟实现3.1strlen函数3.2st

MySQL中COALESCE函数示例详解

《MySQL中COALESCE函数示例详解》COALESCE是一个功能强大且常用的SQL函数,主要用来处理NULL值和实现灵活的值选择策略,能够使查询逻辑更清晰、简洁,:本文主要介绍MySQL中C... 目录语法示例1. 替换 NULL 值2. 用于字段默认值3. 多列优先级4. 结合聚合函数注意事项总结C

Java8需要知道的4个函数式接口简单教程

《Java8需要知道的4个函数式接口简单教程》:本文主要介绍Java8中引入的函数式接口,包括Consumer、Supplier、Predicate和Function,以及它们的用法和特点,文中... 目录什么是函数是接口?Consumer接口定义核心特点注意事项常见用法1.基本用法2.结合andThen链

MySQL 日期时间格式化函数 DATE_FORMAT() 的使用示例详解

《MySQL日期时间格式化函数DATE_FORMAT()的使用示例详解》`DATE_FORMAT()`是MySQL中用于格式化日期时间的函数,本文详细介绍了其语法、格式化字符串的含义以及常见日期... 目录一、DATE_FORMAT()语法二、格式化字符串详解三、常见日期时间格式组合四、业务场景五、总结一、

golang panic 函数用法示例详解

《golangpanic函数用法示例详解》在Go语言中,panic用于触发不可恢复的错误,终止函数执行并逐层向上触发defer,最终若未被recover捕获,程序会崩溃,recover用于在def... 目录1. panic 的作用2. 基本用法3. recover 的使用规则4. 错误处理建议5. 常见错