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

相关文章

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使用了由[90]描述的第一个算法。开发者应该注意,由于数据点靠近包含的 Mat 元素的边界,返回的椭圆/旋转矩形数据

Unity3D 运动之Move函数和translate

CharacterController.Move 移动 function Move (motion : Vector3) : CollisionFlags Description描述 A more complex move function taking absolute movement deltas. 一个更加复杂的运动函数,每次都绝对运动。 Attempts to

✨机器学习笔记(二)—— 线性回归、代价函数、梯度下降

1️⃣线性回归(linear regression) f w , b ( x ) = w x + b f_{w,b}(x) = wx + b fw,b​(x)=wx+b 🎈A linear regression model predicting house prices: 如图是机器学习通过监督学习运用线性回归模型来预测房价的例子,当房屋大小为1250 f e e t 2 feet^

JavaSE(十三)——函数式编程(Lambda表达式、方法引用、Stream流)

函数式编程 函数式编程 是 Java 8 引入的一个重要特性,它允许开发者以函数作为一等公民(first-class citizens)的方式编程,即函数可以作为参数传递给其他函数,也可以作为返回值。 这极大地提高了代码的可读性、可维护性和复用性。函数式编程的核心概念包括高阶函数、Lambda 表达式、函数式接口、流(Streams)和 Optional 类等。 函数式编程的核心是Lambda

PHP APC缓存函数使用教程

APC,全称是Alternative PHP Cache,官方翻译叫”可选PHP缓存”。它为我们提供了缓存和优化PHP的中间代码的框架。 APC的缓存分两部分:系统缓存和用户数据缓存。(Linux APC扩展安装) 系统缓存 它是指APC把PHP文件源码的编译结果缓存起来,然后在每次调用时先对比时间标记。如果未过期,则使用缓存的中间代码运行。默认缓存 3600s(一小时)。但是这样仍会浪费大量C

PHP7扩展开发之函数方式使用lib库

前言 首先说下什么是lib库。lib库就是一个提供特定功能的一个文件。可以把它看成是PHP的一个文件,这个文件提供一些函数方法。只是这个lib库是用c或者c++写的。 使用lib库的场景。一些软件已经提供了lib库,我们就没必要再重复实现一次。如,原先的mysql扩展,就是使用mysql官方的lib库进行的封装。 在本文,我们将建立一个简单的lib库,并在扩展中进行封装调用。 代码 基础