WebGL自学课程(5):使用一张贴图纹理绘制地球
阅读原文时间:2021年04月22日阅读:1

注:转载请注明出处

《WebGL自学课程(3):原生WebGL+ArcGIS JS API绘制旋转地球》一文中讲述了如何利用地图数据绘制地球的轮廓,但是缺少色彩。本文就是想通过贴图的方式让地球穿上一层靓丽的外衣,并可以通过鼠标拖拽等对绘制的地球进行交互式操作。由于本人《WebGL自学课程(4):WebGL矩阵、Camera基础操作》一文中构建了本人自己常用的代码,封装到World.js中,所以在以后的自学课程中都会在文档中引入World.js,以提高开发效率。

本课程所使用的二维贴图如下:

运行效果截图如下:

本文代码.如下:

<!doctype html>
<html>
    <head>
        <title>第一个纹理Demo</title>
        <meta http-equiv="Content-Type" content="text/html" />
        <meta name="charset" content="utf-8"/>
        <style type="text/css">
            html,body,div{margin:0;padding:0}
        </style>
        <script type="text/javascript" src="http://localhost/arcgis_js_api/library/2.7/jsapi/"></script>
        <script type="text/javascript" src="World.js"></script>
        <script id="shader-vs" type="x-shader/x-vertex">
            attribute vec3 aPosition;
            attribute vec2 aTextureCoord;
            varying vec2 vTextureCoord;

            uniform mat4 uModelView;
            uniform mat4 uProj;

            void main()
            {
                gl_Position = uProj * uModelView * vec4(aPosition,1.0);
                vTextureCoord = aTextureCoord;
            }
        </script>
        <script id="shader-fs" type="x-shader/x-fragment">
            precision mediump float;

            varying vec2 vTextureCoord;
            uniform sampler2D uSampler;

            void main()
            {
                gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
            }
        </script>
        <script type="text/javascript">
            var R = 10;
            var canvas = null;
            var gl = null;
            var shaderProgram = null;
            var aPositionLocation;
            var aTextureCoordLocation;
            var uModelViewLocation;
            var uProjLocation;
            var uSamplerLocation;

            var vertexPositionBuffer;
            var textureCoordBuffer;
            var modelMatrix = new Matrix();
            var camera = new PerspectiveCamera(90,1,1.0,200.0);
            camera.look(new Vertice(0,0,1.5*R),new Vertice(0,0,0),new Vector(0,1,0));
            var texture;
            var bImageLoaded = false;

            var bMouseDown = false;
            var handleMouseMove;
            var previousX=-1;
            var previousY=-1;


            function initWebGL(canvas){
                try{
                    gl = canvas.getContext("experimental-webgl",{antialias:true});
                }
                catch(e){
                    alert("浏览器不支持WebGL!");
                }

                if(!gl)
                    alert("浏览器不支持WebGL!");
            }

            function getShader(gl,id){
                var shaderScript = document.getElementById(id);
                if(!shaderScript)
                    return null;

                var shader = null;
                if(shaderScript.type=="x-shader/x-vertex"){
                    shader = gl.createShader(gl.VERTEX_SHADER);
                }
                else if(shaderScript.type=="x-shader/x-fragment"){
                    shader = gl.createShader(gl.FRAGMENT_SHADER);
                }
                else{
                    return null;
                }

                gl.shaderSource(shader,shaderScript.text);
                gl.compileShader(shader);

                if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){
                    alert(gl.getShaderInfoLog(shader));
                    gl.deleteShader(shader);
                    return null;
                }

                return shader;
            }

            function initShaders(){
                var vertexShader = getShader(gl,"shader-vs");
                var fragmentShader = getShader(gl,"shader-fs");

                shaderProgram = gl.createProgram();
                gl.attachShader(shaderProgram,vertexShader);
                gl.attachShader(shaderProgram,fragmentShader);
                gl.linkProgram(shaderProgram);

                if(!gl.getProgramParameter(shaderProgram,gl.LINK_STATUS)){
                    alert("Could not link program");
                    gl.deleteProgram(shaderProgram);
                    gl.deleteShader(vertexShader);
                    gl.deleteShader(fragmentShader);
                    return;
                }

                gl.useProgram(shaderProgram);

                aPositionLocation = gl.getAttribLocation(shaderProgram,"aPosition");
                gl.enableVertexAttribArray(aPositionLocation);

                aTextureCoordLocation = gl.getAttribLocation(shaderProgram,"aTextureCoord");
                gl.enableVertexAttribArray(aTextureCoordLocation);

                uModelViewLocation = gl.getUniformLocation(shaderProgram,"uModelView");
                uProjLocation = gl.getUniformLocation(shaderProgram,"uProj");
                uSamplerLocation = gl.getUniformLocation(shaderProgram,"uSampler");
            }

            function initBuffer(){
                vertexPositionBuffer = gl.createBuffer();                
                textureCoordBuffer = gl.createBuffer();
            }

            function initTexture(name){
                texture = gl.createTexture();
                texture.image = new Image();
                texture.image.onload = function () {
                    handleLoadedTexture(texture);
                };

                texture.image.src = name;
            }

            function handleLoadedTexture(texture) {
                gl.bindTexture(gl.TEXTURE_2D, texture);
                gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
                gl.bindTexture(gl.TEXTURE_2D, null);
                bImageLoaded = true;
            }

            function drawEarth(column,row){
                var eachLog = 180 / column;
                var eachLat = 90 / row;
                for(var i = 0;i < column;i++){
                    for(var j = 0;j < row;j++){
                        var log1 = eachLog * i;
                        var log2 = eachLog * (i+1);
                        var lat1 = eachLat * j;
                        var lat2 = eachLat * (j+1);
                        var p1 = getXYZ(log1,lat1,R);
                        var p2 = getXYZ(log2,lat1,R);
                        var p3 = getXYZ(log1,lat2,R);
                        var p4 = getXYZ(log2,lat2,R);


                        var vertices;
                        var textureCoords;

                        //东北半球
                        vertices = [p1[0],p1[1],p1[2],//左下角点
                                    p2[0],p2[1],p2[2],//右下角点
                                    p3[0],p3[1],p3[2],//左上角点
                                    p4[0],p4[1],p4[2]];//右上角点
                        textureCoords = [0.5+log1/360,0.5+lat1/180,//左下角点
                                         0.5+log2/360,0.5+lat1/180,//右下角点
                                         0.5+log1/360,0.5+lat2/180,//左上角点
                                         0.5+log2/360,0.5+lat2/180];//右上角点
                        drawFace(vertices,textureCoords);

                        //东南半球
                        vertices = [p3[0],-p3[1],p3[2],
                                    p4[0],-p4[1],p4[2],
                                    p1[0],-p1[1],p1[2],
                                    p2[0],-p2[1],p2[2]];
                        textureCoords = [0.5+log1/360,0.5-lat2/180,
                                         0.5+log2/360,0.5-lat2/180,
                                         0.5+log1/360,0.5-lat1/180,
                                         0.5+log2/360,0.5-lat1/180];
                        drawFace(vertices,textureCoords);

                        //西北半球
                        vertices = [-p2[0],p2[1],p2[2],
                                    -p1[0],p1[1],p1[2],
                                    -p4[0],p4[1],p4[2],
                                    -p3[0],p3[1],p3[2]];
                        textureCoords = [0.5-log2/360,0.5+lat1/180,
                                         0.5-log1/360,0.5+lat1/180,
                                         0.5-log2/360,0.5+lat2/180,
                                         0.5-log1/360,0.5+lat2/180];
                        drawFace(vertices,textureCoords);

                        //西南半球
                        vertices = [-p4[0],-p4[1],p4[2],
                                    -p3[0],-p3[1],p3[2],
                                    -p2[0],-p2[1],p2[2],
                                    -p1[0],-p1[1],p1[2]];
                        textureCoords = [0.5-log2/360,0.5-lat2/180,
                                         0.5-log1/360,0.5-lat2/180,
                                         0.5-log2/360,0.5-lat1/180,
                                         0.5-log1/360,0.5-lat1/180];
                        drawFace(vertices,textureCoords);
                    }
                }
            }

            function drawFace(vertices,textureCoords){
                gl.bindBuffer(gl.ARRAY_BUFFER,vertexPositionBuffer);
                gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);
                gl.vertexAttribPointer(aPositionLocation,3,gl.FLOAT,false,0,0);

                gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
                gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(textureCoords),gl.STATIC_DRAW);
                gl.vertexAttribPointer(aTextureCoordLocation,2, gl.FLOAT, false, 0, 0);

                gl.activeTexture(gl.TEXTURE0);
                gl.bindTexture(gl.TEXTURE_2D, texture);
                gl.uniform1i(uSamplerLocation, 0);

                gl.drawArrays(gl.TRIANGLE_STRIP,0,4);
            }

            function getXYZ(longitude,latitude,r){
                var vertice = [];
                var radianLog = Math.PI/180*longitude;
                var radianLat = Math.PI/180*latitude;
                var sin1 = Math.sin(radianLog);
                var cos1 = Math.cos(radianLog);
                var sin2 = Math.sin(radianLat);
                var cos2 = Math.cos(radianLat);
                var x = r*sin1*cos2;
                var y = r*sin2;
                var z = r*cos1*cos2;
                vertice.push(x);
                vertice.push(y);
                vertice.push(z);
                return vertice;
            }

            function drawScene(){
                gl.viewport(0,0,canvas.width,canvas.height);
                gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
                gl.uniformMatrix4fv(uModelViewLocation,false,camera.getViewMatrix().multiply(modelMatrix).elements);                    
                gl.uniformMatrix4fv(uProjLocation,false,camera.projMatrix.elements);                

                if(bImageLoaded){
                    drawEarth(20,10);
                }
            }

            function initRequestAnimationFrame(){
                window.requestAnimationFrame = window.requestAnimationFrame
                                        || window.mozRequestAnimationFrame
                                        || window.webkitRequestAnimationFrame
                                        || window.msRequestAnimationFrame
                                        || window.oRequestAnimationFrame
                                        || function(callback) {
                                            setTimeout(callback, 1000 / 60);
                                        };
            }

            function tick() {               
                window.requestAnimationFrame(tick);
                drawScene();
            }

            function canvasMouseDown(){
                bMouseDown = true;
                handleMouseMove = dojo.connect(dojo.byId("iCanvas"),"onmousemove","canvasMouseMove");
            }

            function canvasMouseMove(e){
                var x = e.layerX||e.offsetX;
                var y = e.layerY||e.offsetY;

                if(previousX > 0 && previousY > 0){
                    var changeX = x - previousX;
                    var changeY = y - previousY;
                    var horCameraAngle = canvas.width / canvas.height * camera.fov;
                    var changeHorAngle = changeX / canvas.width * horCameraAngle;
                    var changeVerAngle = changeY / canvas.height * camera.fov;
                    camera.worldRotateY(-changeHorAngle*Math.PI/180);
                    camera.worldRotateX(-changeVerAngle*Math.PI/180);
                }
                previousX = x;
                previousY = y;
            }

            function canvasMouseUp(){
                bMouseDown = false;
                dojo.disconnect(handleMouseMove);
                previousX = -1;
                previousY = -1;
            }

            function startWebGL(){
                canvas = document.getElementById("iCanvas");
                initWebGL(canvas);
                initShaders();
                initBuffer();
                initTexture("earth.jpg");

                gl.clearColor(0.9,0.9,0.9,1.0);
                gl.enable(gl.DEPTH_TEST);
                gl.depthFunc(gl.LEQUAL);
                gl.enable(gl.CULL_FACE);//一定要启用裁剪,否则显示不出立体感
                gl.cullFace(gl.BACK);//裁剪掉背面
                initRequestAnimationFrame();
                tick();
            }

            function init(){
                dojo.connect(dojo.byId("iCanvas"),"onmousedown","canvasMouseDown");             
                dojo.connect(dojo.byId("iCanvas"),"onmouseup","canvasMouseUp");
                startWebGL();
            }
        </script>
    </head>
    <body οnlοad="init();">
        <canvas id="iCanvas" width="600" height="600" style="margin-left:100px;margin-top:30px;border:1px solid #000;"></canvas>
    </body>
</html>

注:转载请注明出处

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章