# learn-shader **Repository Path**: colincclala/learn-shader ## Basic Information - **Project Name**: learn-shader - **Description**: 学习shader - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-10-19 - **Last Updated**: 2024-12-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 学习shader ## first-shader ``` glsl /** * 它接受 2 个参数: * 一个是 4 维的fragColor,代表输出的像素颜色; * 另一个是 2 维的fragCoord,代表输入的像素坐标。 */ void mainImage(out vec4 fragColor,in vec2 fragCoord){ vec3 color=vec3(1.,0.,0.); // 红色,红色的RGB颜色值为(255,0,0),在GLSL中,我们需要先将颜色原先的值进行归一化操作(除以255)后才能将它正确地输出 fragColor=vec4(color,1.); // fragColor赋值一个 4 维变量,前 3 维就是color这个颜色变量,最后一维是透明度 } ``` ``` glsl // 内置的变量iResolution,代表了画面整体的大小,使用它时一般会取它的xy维度。 // fragCoord的x轴维度,判断如果它小于四分之一的画面长度iResolution.x*.25 if(fragCoord.x0.){ // c=1.; // 白色 // }else{ // c=0.; // 黑色 // } float c=step(0.,d); // 替换if ``` ### smoothstep函数(平滑阶梯函数) - 抗锯齿 ```glsl // 边界值定为edge1和edge2: // 如果目标值x小于边界值edge1,则返回 0; // 如果目标值x大于边界值edge2,则返回 1; // 如果目标值x在 2 个边界值之间,则返回从 0 到 1 平滑过渡的值。 smoothstep(edge1,edge2,x) ``` ### pow函数 - 数字的指数幂 ```glsl pow(4.,3.) // 4 的 3 次方——64 ``` ### SDF函数(符号距离函数) - 它将空间里的一个位置作为输入,并返回该位置到给定形状的距离 - 它的前面还有个“符号”,是因为在形状外的距离为正数(“+”号),在形状内的距离为负数(“-”号),边界处的值恰好为 0。 - [常用的2D图形的SDF函数](https://iquilezles.org/articles/distfunctions2d/) ```glsl // 绘制圆形 float d=length(uv); d-=.5; // 抽象化 float sdCircle(vec2 p,float r) { return length(p)-r; } float d=sdCircle(uv,.5); ``` #### SDF图形变换 #### SDF布尔运算 - SDF函数什么都可以画,有些形状不能单独画出来,需要组合。 - SDF的布尔运算主要有 3 种:并(Union)、交(Intersection)、差(Subtraction)。 ```glsl float opUnion(float d1,float d2) { return min(d1,d2); } float opIntersection(float d1,float d2) { return max(d1,d2); } float opSubtraction(float d1,float d2) { return max(-d1,d2); } ``` #### SDF布尔运算(平滑版) - 运算***粘稠***的感觉 - 比起一般的布尔运算,平滑版布尔运算还多了第三个参数——平滑度k,能够控制平滑的程度。 ```glsl float opSmoothUnion(float d1,float d2,float k){ float h=clamp(.5+.5*(d2-d1)/k,0.,1.); return mix(d2,d1,h)-k*h*(1.-h); } float opSmoothSubtraction(float d1,float d2,float k){ float h=clamp(.5-.5*(d2+d1)/k,0.,1.); return mix(d2,-d1,h)+k*h*(1.-h); } float opSmoothIntersection(float d1,float d2,float k){ float h=clamp(.5-.5*(d2-d1)/k,0.,1.); return mix(d2,d1,h)+k*h*(1.-h); } ``` ### mix函数(混合函数) ```glsl // 实现 #define mix(x,y,t) x*(1.-t)+y*t ``` - 接受 3 个参数:前 2 个参数x和y分别对应 2 个值,最后一个参数t代表混合程度, - 如果t为 0,则值就等于x; - 如果t为 1,则值就等于y, - 如果t为 0 到 1 内的值,则值就等于x与y之间逐渐变化的值。 1. 创建渐变色 2. 图形染色 3. 形状改变效果 ### 极坐标 - Shader的默认坐标系是笛卡尔坐标系(也就是直角坐标系),除了这种坐标系外,另一种坐标系,叫做极坐标系 - 极坐标系的坐标由 2 个维度组成:极角φ和半径r。 ![坐标](polar-coordinates.awebp) ### 随机函数 - 封装好的随机函数 ```glsl highp float random(vec2 co) { highp float a=12.9898; highp float b=78.233; highp float c=43758.5453; highp float dt=dot(co.xy,vec2(a,b)); highp float sn=mod(dt,3.14); return fract(sin(sn)*c); } ``` ### 噪声 - 噪声也是实现“随机”的一种手段,封装好的噪声函数 ```glsl // // GLSL textureless classic 3D noise "cnoise", // with an RSL-style periodic variant "pnoise". // Author: Stefan Gustavson (stefan.gustavson@liu.se) // Version: 2011-10-11 // // Many thanks to Ian McEwan of Ashima Arts for the // ideas for permutation and gradient selection. // // Copyright (c) 2011 Stefan Gustavson. All rights reserved. // Distributed under the MIT license. See LICENSE file. // https://github.com/ashima/webgl-noise // vec3 mod289(vec3 x) { return x-floor(x*(1./289.))*289.; } vec4 mod289(vec4 x) { return x-floor(x*(1./289.))*289.; } vec4 permute(vec4 x) { return mod289(((x*34.)+1.)*x); } vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159-.85373472095314*r; } vec3 fade(vec3 t){ return t*t*t*(t*(t*6.-15.)+10.); } // Classic Perlin noise float cnoise(vec3 P) { vec3 Pi0=floor(P);// Integer part for indexing vec3 Pi1=Pi0+vec3(1.);// Integer part + 1 Pi0=mod289(Pi0); Pi1=mod289(Pi1); vec3 Pf0=fract(P);// Fractional part for interpolation vec3 Pf1=Pf0-vec3(1.);// Fractional part - 1.0 vec4 ix=vec4(Pi0.x,Pi1.x,Pi0.x,Pi1.x); vec4 iy=vec4(Pi0.yy,Pi1.yy); vec4 iz0=Pi0.zzzz; vec4 iz1=Pi1.zzzz; vec4 ixy=permute(permute(ix)+iy); vec4 ixy0=permute(ixy+iz0); vec4 ixy1=permute(ixy+iz1); vec4 gx0=ixy0*(1./7.); vec4 gy0=fract(floor(gx0)*(1./7.))-.5; gx0=fract(gx0); vec4 gz0=vec4(.5)-abs(gx0)-abs(gy0); vec4 sz0=step(gz0,vec4(0.)); gx0-=sz0*(step(0.,gx0)-.5); gy0-=sz0*(step(0.,gy0)-.5); vec4 gx1=ixy1*(1./7.); vec4 gy1=fract(floor(gx1)*(1./7.))-.5; gx1=fract(gx1); vec4 gz1=vec4(.5)-abs(gx1)-abs(gy1); vec4 sz1=step(gz1,vec4(0.)); gx1-=sz1*(step(0.,gx1)-.5); gy1-=sz1*(step(0.,gy1)-.5); vec3 g000=vec3(gx0.x,gy0.x,gz0.x); vec3 g100=vec3(gx0.y,gy0.y,gz0.y); vec3 g010=vec3(gx0.z,gy0.z,gz0.z); vec3 g110=vec3(gx0.w,gy0.w,gz0.w); vec3 g001=vec3(gx1.x,gy1.x,gz1.x); vec3 g101=vec3(gx1.y,gy1.y,gz1.y); vec3 g011=vec3(gx1.z,gy1.z,gz1.z); vec3 g111=vec3(gx1.w,gy1.w,gz1.w); vec4 norm0=taylorInvSqrt(vec4(dot(g000,g000),dot(g010,g010),dot(g100,g100),dot(g110,g110))); g000*=norm0.x; g010*=norm0.y; g100*=norm0.z; g110*=norm0.w; vec4 norm1=taylorInvSqrt(vec4(dot(g001,g001),dot(g011,g011),dot(g101,g101),dot(g111,g111))); g001*=norm1.x; g011*=norm1.y; g101*=norm1.z; g111*=norm1.w; float n000=dot(g000,Pf0); float n100=dot(g100,vec3(Pf1.x,Pf0.yz)); float n010=dot(g010,vec3(Pf0.x,Pf1.y,Pf0.z)); float n110=dot(g110,vec3(Pf1.xy,Pf0.z)); float n001=dot(g001,vec3(Pf0.xy,Pf1.z)); float n101=dot(g101,vec3(Pf1.x,Pf0.y,Pf1.z)); float n011=dot(g011,vec3(Pf0.x,Pf1.yz)); float n111=dot(g111,Pf1); vec3 fade_xyz=fade(Pf0); vec4 n_z=mix(vec4(n000,n100,n010,n110),vec4(n001,n101,n011,n111),fade_xyz.z); vec2 n_yz=mix(n_z.xy,n_z.zw,fade_xyz.y); float n_xyz=mix(n_yz.x,n_yz.y,fade_xyz.x); return 2.2*n_xyz; } ``` ### FBM - FBM全称Fractal Brownian Motion,中译是“分形布朗运动”, - 它是将多个具有不同频率和振幅的噪声的结果值相叠加而产生结果的一种随机过程。 - 主要被用于构造自然界的云层、山脉、地貌等不规则的形体。 ```glsl float fbm(vec3 p){ float value=0.; // value代表结果值、 float amplitude=1.; // amplitude代表振幅、 float frequency=1.; // frequency代表频率, float lacunarity=2.; // lacunarity代表空隙、 float persistance=.5; // persistance代表持续度、 float scale=1.; // scale代表缩放程度、 int octaves=8; // octaves代表音度。 // for循环来叠加噪声,叠加次数就是音度,在叠加的同时升高频率,降低振幅 for(int i=0;i1. 平移 - 相反操作 >2. 缩放 - 相反操作 >3. 翻转 - 按x轴翻转,给UV的y坐标乘上-1 即可。 >4. 旋转 >5. 重复 - 内置函数——***fract***,获取一个数的小数部分。 ```glsl fract(114.514) // 返回值为0.514 uv=fract(uv*vec2(2.,2.)); // 4个图像 坐标整体*2,大于1的部分取小数 uv=fract(uv*vec2(3.,4.)); // 9个图像 坐标整体*4,大于1的部分取小数 ``` - 除了fract函数,sin函数的图像也具有重复的特性。 >6. 镜像 - 内置函数——***abs***,获取一个数的绝对值。 ## Other - [很方便地对Shader的函数进行可视化的调试。](https://graphtoy.com/) - [转场特效](https://github.com/gl-transitions/gl-transitions) - ***glslify*** 是一个将glsl文件转化为js模块的工具,可以直接在js中使用glsl文件 - ***glsl-canvas*** 是一个在浏览器中运行glsl的库,可以直接在js中使用glsl文件 - ***glsl-canvas-editor*** 是一个在浏览器中运行glsl的库,