视觉高级篇19#如何用着色器实现像素动画?(代码片段)

凯小默 凯小默     2023-03-23     546

关键词:

说明

【跟月影学可视化】学习笔记。

如何用着色器实现固定帧动画

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>如何用着色器实现固定帧动画</title>
        <style>
            canvas 
                border: 1px dashed salmon;
            
        </style>
    </head>
    <body>
        <canvas width="512" height="512"></canvas>
        <script src="./common/lib/gl-renderer.js"></script>
        <script>
            const vertex = `
                attribute vec2 a_vertexPosition;
                attribute vec2 uv;

                varying vec2 vUv;

                void main() 
                    gl_PointSize = 1.0;
                    vUv = uv;
                    gl_Position = vec4(a_vertexPosition, 1, 1);
                
            `;

            const fragment = `
                #ifdef GL_ES
                precision highp float;
                #endif

                varying vec2 vUv;
                uniform sampler2D tMap;
                uniform float fWidth;
                uniform vec2 vFrames[3];
                uniform int frameIndex;

                void main() 
                    vec2 uv = vUv;
                    // 动画只有 3 帧,所以最多只需要循环 3 次。
                    for (int i = 0; i < 3; i++) 
                        // vFrames 是每一帧动画的图片起始 x 和结束 x 坐标
                        uv.x = mix(vFrames[i].x, vFrames[i].y, vUv.x) / fWidth;
                        if(float(i) == mod(float(frameIndex), 3.0)) break;
                    
                    vec4 color = texture2D(tMap, uv);
                    gl_FragColor = color;
                
            `;

            const canvas = document.querySelector("canvas");
            const renderer = new GlRenderer(canvas);

            const textureURL = "./assets/img/bird.png";
            (async function () 
                const texture = await renderer.loadTexture(textureURL);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
                renderer.uniforms.tMap = texture;
                renderer.uniforms.fWidth = 272; // 图片的总宽度
                renderer.uniforms.vFrames = [2, 88, 90, 176, 178, 264];
                renderer.uniforms.frameIndex = 0;
                setInterval(() => 
                    renderer.uniforms.frameIndex++;
                , 200);
                const x = 43 / canvas.width;
                const y = 30 / canvas.height;
                renderer.setMeshData([
                    
                        positions: [
                            [-x, -y],
                            [-x, y],
                            [x, y],
                            [x, -y],
                        ],
                        attributes: 
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        ,
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    ,
                ]);
                renderer.render();
            )();
        </script>
    </body>
</html>

如何用着色器实现非固定帧动画

WebGL 有两种 Shader,分别是顶点着色器和片元着色器,它们都可以用来实现动画。在绘制一帧画面的时候,顶点着色器的运算量会大大少于片元着色器,所以使用顶点着色器消耗的性能更少。片元着色器可以使用重复、随机、噪声等技巧来绘制更加复杂的效果。

用顶点着色器实现非固定帧动画

在顶点着色器中,我们直接改变了顶点坐标,所以这样实现的旋转动画和 WebGL 坐标系(右手系)的方向一致,角度增大呈逆时针方向旋转。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>用顶点着色器实现非固定帧动画</title>
        <style>
            canvas 
                border: 1px dashed salmon;
            
        </style>
    </head>
    <body>
        <canvas width="512" height="512"></canvas>
        <script src="./common/lib/gl-renderer.js"></script>
        <script>
            const vertex = `
                attribute vec2 a_vertexPosition;
                attribute vec2 uv;

                varying vec2 vUv;
                uniform float rotation;

                void main() 
                    gl_PointSize = 1.0;
                    vUv = uv;
                    float c = cos(rotation);
                    float s = sin(rotation);
                    mat3 transformMatrix = mat3(
                        c, s, 0,
                        -s, c, 0,
                        0, 0, 1
                    );
                    vec3 pos = transformMatrix * vec3(a_vertexPosition, 1);
                    gl_Position = vec4(pos, 1);
                
            `;

            const fragment = `
                #ifdef GL_ES
                precision highp float;
                #endif

                varying vec2 vUv;
                uniform vec4 color;

                void main() 
                    gl_FragColor = color;
                
            `;

            const canvas = document.querySelector("canvas");
            const renderer = new GlRenderer(canvas);
            const program = renderer.compileSync(fragment, vertex);
            renderer.useProgram(program);
            
            renderer.uniforms.color = [250/255, 128/255, 114/255, 1];
            renderer.uniforms.rotation = 0.0;

            renderer.setMeshData([
                
                    positions: [
                        [-0.5, -0.5],
                        [-0.5, 0.5],
                        [0.5, 0.5],
                        [0.5, -0.5],
                    ],
                    attributes: 
                        uv: [
                            [0, 0],
                            [0, 1],
                            [1, 1],
                            [1, 0],
                        ],
                    ,
                    cells: [
                        [0, 1, 2],
                        [2, 0, 3],
                    ],
                ,
            ]);
            renderer.render();

            function update() 
                renderer.uniforms.rotation += 0.05;
                requestAnimationFrame(update);
            

            update();
        </script>
    </body>
</html>

用片元着色器实现非固定帧动画

在片元着色器中,我们的绘制原理是通过距离场着色来实现的,所以这里的旋转实际上改变的是距离场的角度而不是图形角度,最终绘制的图形也是相对于距离场的。又因为距离场逆时针旋转,所以图形就顺时针旋转了。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>用片元着色器实现非固定帧动画</title>
        <style>
            canvas 
                border: 1px dashed salmon;
            
        </style>
    </head>
    <body>
        <canvas width="512" height="512"></canvas>
        <script src="./common/lib/gl-renderer.js"></script>
        <script>
            const vertex = `
                attribute vec2 a_vertexPosition;
                attribute vec2 uv;

                varying vec2 vUv;

                void main() 
                    gl_PointSize = 1.0;
                    vUv = uv;
                    gl_Position = vec4(a_vertexPosition, 1, 1);
                
            `;

            const fragment = `
                #ifdef GL_ES
                precision highp float;
                #endif

                varying vec2 vUv;
                uniform vec4 color;
                uniform float rotation;

                void main() 
                    vec2 st = 2.0 * (vUv - vec2(0.5));
                    float c = cos(rotation);
                    float s = sin(rotation);
                    mat3 transformMatrix = mat3(
                        c, s, 0,
                        -s, c, 0,
                        0, 0, 1
                    );
                    vec3 pos = transformMatrix * vec3(st, 1.0);
                    float d1 = 1.0 - smoothstep(0.5, 0.505, abs(pos.x));
                    float d2 = 1.0 - smoothstep(0.5, 0.505, abs(pos.y));
                    gl_FragColor = d1 * d2 * color;
                
            `;

            const canvas = document.querySelector("canvas");
            const renderer = new GlRenderer(canvas);
            const program = renderer.compileSync(fragment, vertex);
            renderer.useProgram(program);
            
            renderer.uniforms.color = [250/255, 128/255, 114/255, 1];
            renderer.uniforms.rotation = 0.0;

            renderer.setMeshData([
                
                    positions: [
                        [-1, -1],
                        [-1, 1],
                        [1, 1],
                        [1, -1],
                    ],
                    attributes: 
                        uv: [
                            [0, 0],
                            [0, 1],
                            [1, 1],
                            [1, 0],
                        ],
                    ,
                    cells: [
                        [0, 1, 2],
                        [2, 0, 3],
                    ],
                ,
            ]);
            renderer.render();

            function update() 
                renderer.uniforms.rotation += 0.05;
                requestAnimationFrame(update);
            

            update();
        </script>
    </body>
</html>

绘制大量重复的旋转正方形

利用网格实现了大量的重复动画,充分利用了 GPU 的并行效率,比用其他方式把图形一个一个地绘制出来性能要高得多。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>大量重复的旋转正方形</title>
        <style>
            canvas 
                border: 1px dashed salmon;
            
        </style>
    </head>
    <body>
        <canvas width="512" height="512"></canvas>
        <script src="./common/lib/gl-renderer.js"></script>
        <script>
            const vertex = `
                attribute vec2 a_vertexPosition;
                attribute vec2 uv;

                varying vec2 vUv;

                void main() 
                    gl_PointSize = 1.0;
                    vUv = uv;
                    gl_Position = vec4(a_vertexPosition, 1, 1);
                
            `;

            const fragment = `
                #ifdef GL_ES
                precision highp float;
                #endif

                varying vec2 vUv;
                uniform float rotation;

                float random (vec2 st) 
                    return fract(sin(dot(st.xy, vec2(12.9898,78.233)))*43758.5453123);
                

                vec3 hsb2rgb(vec3 c)
                    vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), 6.0)-3.0)-1.0, 0.0, 1.0);
                    rgb = rgb * rgb * (3.0 - 2.0 * rgb);
                    return c.z * mix(vec3(1.0), rgb, c.y);
                

                void main() 
                    vec2 f_uv = fract(vUv * 10.0);
                    vec2 i_uv = floor(vUv * 10.0);
                    vec2 st = 2.0 * (f_uv - vec2(0.5));
                    float c = 0.7 * cos(rotation);
                    float s = 0.7 * sin(rotation);
                    mat3 transformMatrix = mat3(
                        c, s, 0,
                        -s, c, 0,
                        0, 0, 1
                    );
                    vec3 pos = transformMatrix * vec3(st, 1.0);
                    float d1 = 1.0 - smoothstep(0.5, 0.505, abs(pos.x));
                    float d2 = 1.0 - smoothstep(0.5, 0.505, abs(pos.y));
                    gl_FragColor = d1 * d2 * vec4(hsb2rgb(vec3(random(i_uv), 1.0, 1.0)), 1.0);
                
            `;

            const canvas = document.querySelector("canvas");
            const renderer = new GlRenderer(canvas);
            const program = renderer.compileSync(fragment, vertex);
            renderer.useProgram(program);
            
            renderer.uniforms.rotation = 0.0;

            renderer.setMeshData([
                
                    positions: [
                        [-1, -1],
                        [-1, 1],
                        [1, 1],
                        [1, -1],
                    ],
                    attributes: 
                        uv: [
                            [0, 0],
                            [0, 1],
                            [1, 1],
                            [1, 0],
                        ],
                    ,
                    cells: [
                        [0, 1, 2],
                        [2, 0, 3],
                    ],
                ,
            ]);
            renderer.render();

            function update() 
                renderer.uniforms.rotation += 0.05;
                requestAnimationFrame(update);
            

            update();
        </script>
    </body>
</html>

如何在着色器中实现缓动函数与非线性插值

实现轨迹动画

<!DOCTYPE html>
<html lang="视觉高级篇25#如何用法线贴图模拟真实物体表面(代码片段)

...同的表面,但实际上它又只是一个光滑的平面。对于视觉效果而言,它的效率比原有的凹凸表面更高,若在特定位置上应用光源,可以让细节程 查看详情

视觉高级篇22#如何用仿射变换来移动和旋转3d物体?(代码片段)

说明【跟月影学可视化】学习笔记。三维仿射变换:平移对于平移变换来说,如果向量P(x0​x_0​x0​​,y0y_0y0​​,z0​z_0​z0​​)沿着向量Q(x1x_1x1​​,y1​y_1​y1​​,z1​z_1​z1​​)平移,只需要让P加上Q,就能得... 查看详情

视觉高级篇22#如何用仿射变换来移动和旋转3d物体?(代码片段)

说明【跟月影学可视化】学习笔记。三维仿射变换:平移对于平移变换来说,如果向量P(x0​x_0​x0​​,y0y_0y0​​,z0​z_0​z0​​)沿着向量Q(x1x_1x1​​,y1​y_1​y1​​,z1​z_1​z1​​)平移,只需要让P加上Q,就能得... 查看详情

wpf像素着色器入门:使用shazzamshadereditor编写hlsl像素着色器代码(代码片段)

...haderEditor编写HLSL像素着色器代码HLSL,HighLevelShaderLanguage,高级着色器语言,是Direct3D着色器模型所必须的语言。WPF支持Direct3D9,也支持使用HLSL来编写着色器。你可以使用任何一款编辑器来编写HLSL,但ShazzamShaderEditor则是专门为WPF... 查看详情

opengl3:高级篇glsl

一.简介这个世界有两种着色器(Shader):  Vertexshaders–在你的场景中,每个顶点都需要调用的程序,称为“顶点着色器”。假如你在渲染一个简单的场景:一个长方形,每个角只有一个顶点。于是vertexshader会被调用... 查看详情

视觉高级篇18#如何生成简单动画让图形动起来?(代码片段)

说明【跟月影学可视化】学习笔记。动画的三种形式固定帧动画:预先准备好要播放的静态图像,然后将这些图依次播放,实现起来最简单,只需要为每一帧准备一张图片,然后循环播放即可。增量动画:... 查看详情

shader是啥意思

...限制。相关例句:1、Visuallydesigningshaderprogramsandeffectfiles.视觉设计着色程序和效果文件。2、Fixedvisualartefactsinbuildingplacingshader.固定的视觉文物建筑放置着色。3、Isetacompositionshaderintothediffusechannel.我在传播路径中设置一个构图着色... 查看详情

directx11withwindowssdk--26计算着色器:入门(代码片段)

前言现在开始迎来所谓的高级篇了,目前计划是计算着色器部分的内容视项目情况,大概会分3-5章来讲述。DirectX11WithWindowsSDK完整目录Github项目源码欢迎加入QQ群:727623616可以一起探讨DX11,以及有什么问题也可以在这里汇报。概述... 查看详情

渲染管道光栅阶段四“片元着色器”

...为每个片元计算颜色,包括光照计算以及阴影处理等高级效果。如何计算由开发者配置,比如通过三角形重心坐标插值计算颜色值,计算阴影ÿ 查看详情

视觉高级篇26#如何绘制带宽度的曲线?(代码片段)

说明【跟月影学可视化】学习笔记。如何用Canvas2D绘制带宽度的曲线?Canvas2D提供了相应的API,能够绘制出不同宽度、具有特定连线方式(lineJoin)和线帽形状(lineCap)的曲线,绘制曲线非常简单。什么... 查看详情

视觉基础篇17#如何使用后期处理通道增强图像效果?(代码片段)

说明【跟月影学可视化】学习笔记。为什么需要后期处理通道?由于GPU是并行渲染的,所以在着色器的执行中,每个像素的着色都是同时进行的,彼此独立的,不能共享信息,就不能获得某一个像素坐标周... 查看详情

视觉基础篇15#如何用极坐标系绘制有趣图案?(代码片段)

说明【跟月影学可视化】学习笔记。极坐标示意图极坐标系使用相对极点的距离,以及与x轴正向的夹角来表示点的坐标,如(3,60°)。直角坐标和极坐标相互转换//直角坐标影射为极坐标functiontoPolar(x,y)constr&... 查看详情

视觉高级篇25#如何用法线贴图模拟真实物体表面(代码片段)

...同的表面,但实际上它又只是一个光滑的平面。对于视觉效果而言,它的效率比原有的凹凸表面更高,若在特定位置上应用光源,可以让细节程度较低的表面生成高细节程度的精确光照方向和反射效果。什么是切... 查看详情

渲染管道光栅阶段四“片元着色器”

...为每个片元计算颜色,包括光照计算以及阴影处理等高级效果。如何计算由开发者配置,比如通过三角形重心坐标插值计算颜色值,计算阴影,或者直接用纹理覆盖。可编程,可选。片元经过一系列的测试ÿ... 查看详情

如何在 C++ 中获取像素着色器版本和顶点着色器版本

】如何在C++中获取像素着色器版本和顶点着色器版本【英文标题】:Howtogetpixelshaderversionandvertexshaderversioninc++【发布时间】:2018-05-2806:58:56【问题描述】:如标题中所述,我需要在C++代码中获取像素着色器版本和顶点着色器版本... 查看详情

对于 OpenGL 着色器,您将如何用 C++ 编写一个接受所有类型的统一函数?

】对于OpenGL着色器,您将如何用C++编写一个接受所有类型的统一函数?【英文标题】:ForOpenGLShaders,howwouldyouwriteaUniformFunctioninC++thatacceptsalltypes?【发布时间】:2019-11-1419:08:41【问题描述】:假设我有一个Shader类,我想要一个Uniform... 查看详情

[unityshader]逐顶点光照和逐片元光照

...。  前一种算法是根据漫反射公式计算顶点颜色(顶点着色器),对颜色插值(光栅化过程)返回每个像素的颜色值(片元着色器)。  第二种算法是获得顶点的法线(顶点着色器),对法线插值(光栅化过程),根据漫反... 查看详情

hlsl像素着色器

原文:HLSL像素着色器 昨日不可追,?今日尤可为.勤奋,炽诚,不忘初心手机淘宝二维码?扫描??????或者打开连接:程序设计开发?,掌声鼓励,欢迎光临.??像素着色器替代了固定渲染管线的?多纹理化?阶段(书上说的)这是片面... 查看详情