利用貝塞爾曲線和着色器來寫一個飛線


為了加深自己對着色器語言的認識,於是就着手寫了一個簡版的"飛線"。

做3D的或者做可視化的應該對這個詞不陌生,一般會用在地理方面的3D需求上,廢話不多說,先上今天的demo的gif圖示:

看完效果,讓我們繼續一步一步地看是怎么實現的

一、寫在着色器之前

三部分:

1⃣️地球

    // 添加地球
    var globeMesh;
    function addglobe() {
        var globeTextureLoader = new THREE.TextureLoader();
        globeTextureLoader.load('../texture/earth.jpeg', function (texture1) {
            console.log(texture1)
            var globeGgeometry = new THREE.SphereGeometry(60, 100, 100);
            var globeMaterial = new THREE.MeshStandardMaterial({map: texture1});
            globeMesh = new THREE.Mesh(globeGgeometry, globeMaterial);
            scene.add(globeMesh);
        });
    }

地球的旋轉,直接在每次渲染的時候改rotation就好了,這里不啰嗦。

2⃣️路徑線

    var flyline;
    function addline(){
        var curve = new THREE.CubicBezierCurve3(
            new THREE.Vector3( -70, 0, 0 ),
            new THREE.Vector3( -35, 100, 0 ),
            new THREE.Vector3( 35, 100, 0 ),
            new THREE.Vector3( 70, 0, 0 )
        );
        var points = curve.getPoints( 50 );

        var geometry = new THREE.BufferGeometry().setFromPoints( points );
        var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
        flyline = new THREE.Line( geometry, material );

        scene.add(flyline);
    }

利用three.js提供的貝塞爾曲線類結合材質生成了上面你所看到的貝塞爾曲線。

3⃣️夜空背景

    var scene;
    function initScene() {
        scene = new THREE.Scene();
        var bgTexture = new THREE.TextureLoader().load("../texture/starfiled.jpeg");
        scene.background = bgTexture;
    }

不難看出是給場景添加了一個背景貼圖。

二、線條的材質替換成shader材質

我們可以看到,目前線條的材質是帶有特定顏色的 THREE.LineBasicMaterial 材質。現在我們要將其換成shader材質。

    //創建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}
        };

        var meshMaterial = new THREE.ShaderMaterial({
            uniforms: uniforms,
            defaultAttributeValues : attributes,
            vertexShader: vertShader,
            fragmentShader: fragShader,
            transparent: true
        });
        return meshMaterial;
    }
    

這段生成shader材質的代碼其實很簡單,注釋都有,就不啰嗦了,因為這個簡要的demo所用到的動畫只需要一個參數,那么我們就只傳一個參數吧(time)。

頂點着色器代碼:

<!-- 頂點着色器 -->
<script id="vertex-shader" type="x-shader/x-vertex">

    void main(){
        vec3 posChanged = position;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(posChanged,1.0);
    }

</script>

代碼只做了一件事,將物體頂點進行了矩陣變換,轉化為屏幕上的點(上一篇博客也有)。

片元着色器代碼:

<script id="fragment-shader-7" type="x-shader/x-fragment">
    uniform float time;
void main( void ) { float start = time; float end = start + 20.0; float opacity = 0.0;
if(gl_FragCoord.x > start && gl_FragCoord.x < end){ opacity = 1.0; } gl_FragColor = vec4(1.0,1.0,1.0,opacity); } </script>

因為片元着色器逐點繪制,所以對於gl_FragCoord這個內置變量,表示的就是屏幕上的點(一個window下,iframe下也是一個單獨window);

gl_FragCoord 坐標是以左下角為(0,0),右上角為(屏寬,屏高),拿上述iframe來說,右上角就是(966,772);其實更准確的說法請參照下面這段話:

但今天不是來研究這些的,我們繼續看着色器代碼。

因為視圖是每一幀繪制一次,假如現在是第199幀(time+=1.0 了199次,此時是200.0)

void main( void ) {
        float start = time;  //200 float end = start + 20.0;  //220 float opacity = 0.0;

        if(gl_FragCoord.x > start && gl_FragCoord.x < end){
            opacity = 1.0;
        }
        gl_FragColor = vec4(1.0,1.0,1.0,opacity);

    }

那么,上述曲線顯示的部分,左端點的x軸就是200像素點,右端點的x軸就是220像素點。針對着色器傳過來的頂點(逐個遍歷),頂點x不在該范圍內的頂點的顏色透明度都是0,也就是看不到,

到了下一幀(time值變化),這個可視區域的范圍就又就變了,於是一幀一幀合起來就重現了我們剛開始看到的那張gif圖:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM