zoukankan      html  css  js  c++  java
  • GPU编程shader之正余弦波和幂/指数函数

    先上一个demo代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style>
            html, body {
                margin: 0;
                height: 100%;
            }
            canvas {
                display: block;
            }
        </style>
    </head>
    <body>
    <script src="../lib/three.min.js"></script>
    <script src="../lib/stats.min.js"></script>
    <script src="../lib/OrbitControl.js"></script>
    
    <!-- 顶点着色器 -->
    <script id="vertex-shader" type="x-shader/x-vertex">
        void main(){
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
        }
    </script>
    
    <script id="fragment-shader-8" type="x-shader/x-fragment">
        uniform float time;
        uniform vec2 resolution;
    
        void main( void ) {
            float x = gl_FragCoord.x;
            float y = gl_FragCoord.y;
            float fy = sin(x / 100. + time * 5.) * 100. + 250.;
            float opacity = 0.0;
            if(y > fy){
                opacity = 1.0;
            }
            gl_FragColor = vec4(1.,1.,1.,opacity);
        }
    </script>
    
    
    
    
        <script type="module">
        var renderer;
        var clock ;
        function initRender() {
            clock = new THREE.Clock();
            renderer = new THREE.WebGLRenderer({antialias: true,alpha:true});
            renderer.setSize(window.innerWidth, window.innerHeight);
            //告诉渲染器需要阴影效果
            //renderer.shadowMap.enabled = true;
            //renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是,没有设置的这个清晰 THREE.PCFShadowMap
            renderer.setClearColor(0xffffff);
            document.body.appendChild(renderer.domElement);
        }
    
        var camera;
        function initCamera() {
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(0, 0, 150);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
        }
    
        var scene;
        function initScene() {
            scene = new THREE.Scene();
            var bgTexture = new THREE.TextureLoader().load("../texture/starfiled.jpeg");
            scene.background = bgTexture;
        }
    
        function initLight() {
            var hemisphereLight1 = new THREE.HemisphereLight(0xffffff, 0x444444, 2);
            hemisphereLight1.position.set(0, 200, 0);
            scene.add(hemisphereLight1);
        }
    
    
    
        var plane;
        function addplane(){
            var planeGeometry = new THREE.PlaneGeometry(450,250)
            var meshMaterial = createMaterial("vertex-shader", "fragment-shader-8");
    
            plane = new THREE.Mesh(planeGeometry,meshMaterial);
            scene.add(plane);
        }
    
        //创建ShaderMaterial纹理的函数
        function createMaterial(vertexShader, fragmentShader) {
            var vertShader = document.getElementById(vertexShader).innerHTML; //获取顶点着色器的代码
            var fragShader = document.getElementById(fragmentShader).innerHTML; //获取片元着色器的代码
            //配置着色器里面的attribute变量的值
            var attributes = {};
            //配置着色器里面的uniform变量的值
            var uniforms = {
                time: {type: 'f', value: 1.0},
                scale: {type: 'f', value: 0.2},
                alpha: {type: 'f', value: 0.6},
                resolution: {type: "v2", value: new THREE.Vector2(window.innerWidth, window.innerHeight)}
            };
    
            var meshMaterial = new THREE.ShaderMaterial({
                uniforms: uniforms,
                defaultAttributeValues : attributes,
                vertexShader: vertShader,
                fragmentShader: fragShader,
                transparent: true
            });
            return meshMaterial;
        }
        
        //初始化性能插件
        var stats;
        function initStats() {
            stats = new Stats();
            document.body.appendChild(stats.dom);
        }
    
        //用户交互插件 鼠标左键按住旋转,右键按住平移,滚轮缩放
        var controls;
        function initControls() {
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            // 如果使用animate方法时,将此函数删除
            //controls.addEventListener( 'change', render );
            // 使动画循环使用时阻尼或自转 意思是否有惯性
            controls.enableDamping = true;
            //动态阻尼系数 就是鼠标拖拽旋转灵敏度
            //controls.dampingFactor = 0.25;
            //是否可以缩放
            controls.enableZoom = true;
            //是否自动旋转
            controls.autoRotate = false;
            controls.autoRotateSpeed = 3;
            //设置相机距离原点的最远距离
            controls.minDistance = 1;
            //设置相机距离原点的最远距离
            controls.maxDistance = 200;
            //是否开启右键拖拽
            controls.enablePan = true;
        }
    
        function render() {
            var delta = clock.getDelta();
            renderer.render(scene, camera);
            if(plane){
            plane.material.uniforms.time.value += 0.01;
            }
        }
        //窗口变动触发的函数
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            render();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }
        function animate() {
            //更新控制器
            render();
            //更新性能插件
            stats.update();
            controls.update();
            requestAnimationFrame(animate);
        }
        function draw() {
            initScene();
            initCamera();
            initLight();
            initRender();
    
    
            addplane();
    
            initControls();
            initStats();
            animate();
            window.onresize = onWindowResize;
        }
        draw();
    
        </script>
        
    </body>
    </html>
    View Code

    一、页面结构介绍

    后续的shader代码效果,也基本上会基于上述html页面代码;

    我们看到,我们是用three的一个PlaneGeometry类画了一张面板(你可以把它看作我们今后写shader的画板),然后在这个面板上应用shader材质,这个材质呈现什么样的效果全靠我们去写这个shader啦。比如说,上述代码就是写了一个正余弦波,并且利用一个递增的变量实现波的移动效果。

    虽然这个shader很简单,但是还是有必要解析一下,顺便温习一下高中学的三角函数。

    二、demo中着色器代码解析

    <!-- 顶点着色器 -->
    <script id="vertex-shader" type="x-shader/x-vertex">
        void main(){
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
        }
    </script>
    
    <!-- 片元着色器 -->
    <script id="fragment-shader-8" type="x-shader/x-fragment">
        uniform float time;
        uniform vec2 resolution;
    
        void main( void ) {
            float x = gl_FragCoord.x;
            float y = gl_FragCoord.y;
            float fy = sin(x / 100. + time * 5.) * 100. + 250.;
            float opacity = 0.0;
            if(y > fy){
                opacity = 1.0;
            }
            gl_FragColor = vec4(1.,1.,1.,opacity);
        }
    </script>

    顶点着色器就是把plane的顶点通过矩阵变换转成屏幕上的像素点;

    然后在片元着色器中对这些顶点进行逐个着色(如果有1w个顶点,那么这个片元着色器代码就会跑1w次,因为是在GPU中并行地跑,所以很快),gl_FragCoord就是当前操作的顶点,而gl_FragColor算出来的颜色就是用来在该顶点输出到屏幕的颜色值;

    所以片元着色器就是给你一个顶点gl_FragCoord(屏幕上的像素点),你来决定输出什么颜色gl_FragColor!

    我们看这段main函数里面的代码:

    float fy = sin(x / 100. + time * 5.) * 100. + 250.;
    是不是很熟悉?

    ok,到这里,正弦波就算出来了,但是颜色呢?因为根据每个x,我们算出对应正弦波上的y值,对应屏幕上每个x来说,都有n个y与之对应,那么上面的做法是,在正弦波上面的这些点的颜色的透明通道值为1.0,在下面的这些点的颜色的透明通道值为0,然后再加上横轴的偏移量θ,这里是变量time,由于time是变化的,所以就出现了上图所示的效果。

    以此类推,我们还能写出指数函数、幂函数等的图像

    二、幂函数

    顶点着色器共用的,就不贴了

    <!-- 片元着色器 -->
    <script id="fragment-shader-8" type="x-shader/x-fragment">
        uniform float time;
        uniform vec2 resolution;
    
        void main( void ) {
            float x = gl_FragCoord.x;
            float y = gl_FragCoord.y;
            float fy = pow((x - time * 50.)/10.,2.0) + 200.;
            float opacity = 0.0;
            if(y > fy){
                opacity = 1.0;
            }
            gl_FragColor = vec4(1.,1.,1.,opacity);
        }
    </script>

    其余的曲线方程如果你有兴趣可以自己试着写写看

  • 相关阅读:
    C语言实现字母接龙的小程序
    求教有关C++中子对象析构的问题
    Leaflet中使用leafletsidebar插件实现侧边栏效果
    Leaflet中使用LeafletMiniMap插件实现小地图效果
    Leaflet中使用leafletsearch插件实现搜索定位效果
    Leaflet中使用awesomemarkers插件显示带图标的marker
    Leaflet中使用Leaflet.Spin插件实现地图加载等待效果
    Leaflet中使用Leaflet.Pin插件实现图层要素编辑效果
    Leaflet中使用Leaflet.contextmenu插件实现地图上添加鼠标右键菜单
    Leaflet中使用Leaflet.MagnifyingGlass实现放大镜效果
  • 原文地址:https://www.cnblogs.com/eco-just/p/11198514.html
Copyright © 2011-2022 走看看