使用webGL实现的全景图demo

使用webGL实现的全景图demo

核心代码

(function(exports) {
    var vertex = [];
    var ind = [];
    var step = 2;
    var radius = 400;
    var pi = Math.PI;
    var jstep = 360 / step + 1;
    var sinj, cosj, sini, cosi, sum, sumji;
    var clientrect = document.documentElement.getBoundingClientRect();
    document.addEventListener('touchmove', function(event) {
        event.preventDefault();
    });
    //计算球型的顶点数据 x y z s t
    for (var j = -90, jl = 90; j <= jl; j += step) {
        for (var i = 0, il = 360; i <= il; i += step) {
            sini = Math.sin(i / 180 * pi);
            cosi = Math.cos(i / 180 * pi);
            sinj = Math.sin(j / 180 * pi);
            cosj = Math.cos(j / 180 * pi);
            // 动态计算球型三维坐标 x y z
            vertex.push(radius * cosj * cosi, radius * sinj, radius * cosj * sini);
            // 动态计算球型贴图纹理坐标 uv 或者 st
            vertex.push(i / 360, (j / 90) / 2 + 0.5);
        };
    };
    //计算球型的顶点数据索引
    for (i = 0, il = 360 / step; i < il; ++i) {
        for (j = 0, jl = 180 / step; j < jl; j++) {
            sumji = j * jstep + i;
            sum = (j + 1) * jstep + i + 1;
            if (j == 0) {
                ind.push(sumji, sum, (j + 1) * jstep + i);
            } else if (j == jl - 1) {
                ind.push(sumji, sumji + 1, sum);
            } else {
                ind.push(sumji, sumji + 1, sum);
                ind.push(sumji, sum, (j + 1) * jstep + i);
            };
        };
    };
    exports.config = {
        'width': clientrect.width,
        'height': clientrect.height,
        'indices': new Uint16Array(ind),
        'vertices': new Float32Array(vertex)
    };
})(typeof exports === 'object' ? exports : window);
var gl, dots, video, bitmapdata, canvas = document.getElementById('canvas');
var gltexture, AMORTIZATION = 0.95;
var dY = 0;
var dX = 0;
var old_x, old_y, drag = false;
var rotationX = 0;
var rotationY = 0;
var v_matrix, m_matrix, p_matrix;
var distance = 70;
var rotx = 0;
var roty = 0;

function createplay() {
    var videodata, mp4source;
    video = document.createElement("video");
    video.autoplay = "autoplay";
    video.controls = "controls";
    video.setAttribute("webkit-playsinline", "true"); //解决微信可以不用全屏播放
    mp4source = document.createElement("source");
    mp4source.type = "video/mp4";
    mp4source.src = "./video/utvr.mp4";
    video.appendChild(mp4source);
    video.load();
    videodata = {
        win: {
            start: 0,
            length: 10
        }
    };

    function timeupdatefun() {
        if (this.currentTime > 0) {
            video.removeEventListener("timeupdate", timeupdatefun);
            createpgl();
        };
    };
    video.addEventListener("play", function(event) {
        videodata["win"]["length"] = this.duration - 31;
    });
    video.addEventListener("ended", function(event) {
        // video.load();
        // video.play();
    });
    video.addEventListener("timeupdate", timeupdatefun);
    video.play();
};

function initvertexbuffers(gl, program, vertices, indices, size) {
    var n, uv, fsize, position, indexbuffer, vertexbuffer;
    if (!(gl && vertices.length > 0 && indices.length > 0)) {
        console.log("传入参数错误");
        return 0;
    };
    n = indices.length;
    //获取元素字节大小
    fsize = vertices.BYTES_PER_ELEMENT;
    //创建一个gl的顶点坐标与颜色缓冲区
    vertexbuffer = gl.createBuffer();
    //创建一个gl顶点索引缓冲区
    indexbuffer = gl.createBuffer();
    if (!vertexbuffer || !indexbuffer) {
        console.log("创建缓冲区对象失败");
        return 0;
    };
    //将顶点坐标与颜色缓冲区对象绑定到gl
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexbuffer);
    //将顶点坐标与颜色写入缓冲区对象
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
    //获取顶点着色器变量
    position = Utils.GetGpuLocation(gl, program, "a_position");
    uv = Utils.GetGpuLocation(gl, program, "uv");
    if (position < 0 || uv < 0) {
        gl.deleteBuffer(vertexbuffer);
        gl.deleteBuffer(indexbuffer);
        console.log("获取WebGl顶点变量失败");
        return 0;
    };
    //将缓冲区对象分配给着色器变量 坐标数据
    gl.vertexAttribPointer(position, 3, gl.FLOAT, false, fsize * size, 0);
    //将顶点变量与分配的缓冲区对象连接起来
    gl.enableVertexAttribArray(position);
    //将缓冲区对象分配给着色器变量
    gl.vertexAttribPointer(uv, 2, gl.FLOAT, false, fsize * size, fsize * 3);
    //将顶点变量与分配的缓冲区对象连接起来
    gl.enableVertexAttribArray(uv);
    //将顶点索引缓冲区对象绑定到gl
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexbuffer);
    //将顶点索引写入缓冲区对象
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
    return n;
};

function inittextures(gl, program, name, bitmap, index) {
    //创建纹理对象
    var u_sampler, texture = gl.createTexture();
    var index = index || 0;
    if (!texture) {
        console.log("创建纹理对象失败");
        return;
    };
    u_sampler = Utils.GetGpuLocation(gl, program, name, true);
    if (u_sampler < 0) {
        gl.deleteTexture(texture);
        console.log("获取WebGl片源变量u_Sampler失败");
        return;
    };
    //将纹理图片反转
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    //开启0号纹理单元
    gl.activeTexture(gl["TEXTURE" + index]);
    //将创建的纹理单元绑定
    gl.bindTexture(gl.TEXTURE_2D, texture);
    //配置纹理参数
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    //将img绑定到纹理
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
    //将0号纹理传递给着色器
    gl.uniform1i(u_sampler, index);
    return texture;
};

function drawgl() {
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    //清理之前颜色和深度模型
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    //video.paused || gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
    mat4.identity(mmatrix); //重置模型矩阵
    mat4.rotateX(mmatrix, mmatrix, roty);
    mat4.rotateY(mmatrix, mmatrix, rotx);

    mat4.identity(vmatrix); //视图模型矩阵
    mat4.lookAt(vmatrix, new Float32Array([0, 0, distance]), new Float32Array([0, 0, 1000]), new Float32Array([0, 1, 0]));

    gl.uniformMatrix4fv(v_matrix, false, vmatrix);

    gl.uniformMatrix4fv(m_matrix, false, mmatrix);
    //绘制一个点
    gl.drawElements(gl.TRIANGLES, dots, gl.UNSIGNED_SHORT, 0);
    //清除马上执行
    gl.flush();
    requestAnimationFrame(drawgl, canvas);
};

function mousecall() {
    var beta, old_beta = 0;

    function mousedown(ev) {
        drag = true;
        event.preventDefault();
        ev = (ev.touches && ev.touches[0]) || ev;
        old_x = ev.clientX, old_y = ev.clientY;
        return false;
    };

    function mouseup(ev) {
        drag = false;
    };

    function mousemove(ev) {
        if (!drag) return false;
        ev.preventDefault();
        ev = (ev.touches && ev.touches[0]) || ev;
        dX = ev.clientX;
        dY = ev.clientY;
        rotationX += (old_x - dX) / 4;
        rotationY -= (old_y - dY) / 4;
        if (rotationX >= 360) rotationX -= 360;
        if (rotationX < 0) rotationX += 360;
        rotationY = Math.min(rotationY, 90);
        rotationY = Math.max(rotationY, -90);
        rotx = rotationX * Math.PI / 180;
        roty = rotationY * Math.PI / 180;
        old_x = dX;
        old_y = dY;
    };

    function mousewheel(ev) {
        var delta = ev.detail ? -ev.detail / 3 : ev.wheelDelta / 120;
        distance -= delta * 4;
        distance = Math.min(distance, 900);
        distance = Math.max(distance, -300);
    };

    function resizecall(ev) {
        var clientrect = document.documentElement.getBoundingClientRect();
        canvas.setAttribute('width', clientrect.width);
        canvas.setAttribute('height', clientrect.height);
        gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    };

    function orientationhandler(ev) {
        if (ev.beta != null) {
            beta = ev.beta.toFixed(2);
            if (Math.abs(Math.abs(beta) - Math.abs(old_beta)) > 0.98) {
                rotationY = old_beta = beta - 90;
            };
            rotx = rotationX * Math.PI / 180;
            roty = rotationY * Math.PI / 180;
        };
    };
    canvas.addEventListener("mousedown", mousedown, false);
    canvas.addEventListener("mouseup", mouseup, false);
    canvas.addEventListener("mouseout", mouseup, false);
    canvas.addEventListener("mousemove", mousemove, false);
    canvas.addEventListener("mousewheel", mousewheel, false);
    canvas.addEventListener('DOMMouseScroll', mousewheel, false);

    canvas.addEventListener("touchstart", mousedown, false);
    canvas.addEventListener("touchend", mouseup, false);
    canvas.addEventListener("touchmove", mousemove, false);
    window.addEventListener("resize", resizecall, false);
    if (window.DeviceOrientationEvent) {
        window.addEventListener("deviceorientation", orientationhandler, false);
    };
};

function createpgl() {
    //获取顶点着色代码并且编译生成
    var shadervs = Utils.GetShader(gl, 'shader-vs');
    //获取片元着色代码并且编译生成
    var shaderfs = Utils.GetShader(gl, 'shader-fs');
    //将顶点着色与片元着色交由着色器生成
    var program = Utils.InitShaders(gl, shadervs, shaderfs);
    if (!program) {
        console.log("生成着色器失败!");
        return;
    };
    //返回渲染顶点数量
    dots = initvertexbuffers(gl, program, config.vertices, config.indices, 5);
    //渲染视频纹理
    gltexture = inittextures(gl, program, "sampler", video, 0);
    if (!gltexture) {
        console.log("设置纹理0号失败");
        return;
    };
    v_matrix = Utils.GetGpuLocation(gl, program, 'v_matrix', true);
    m_matrix = Utils.GetGpuLocation(gl, program, 'm_matrix', true);
    p_matrix = Utils.GetGpuLocation(gl, program, 'p_matrix', true);
    if (v_matrix < 0 || m_matrix < 0 || p_matrix < 0) {
        console.log("获取WebGl顶点矩阵变量失败");
        return;
    };
    vmatrix = mat4.create(); //视图矩阵
    mmatrix = mat4.create(); //模型矩阵
    pmatrix = mat4.create(); //投影矩阵
    mat4.rotateY(mmatrix, mmatrix, rotx);
    mat4.rotateX(mmatrix, mmatrix, roty);
    mat4.lookAt(vmatrix, new Float32Array([0, 0, distance]), new Float32Array([0, 0, 1000]), new Float32Array([0, 1, 0]));
    mat4.perspective(pmatrix, Math.PI * 60 / 180, config.width / config.height, 1, 800);

    gl.uniformMatrix4fv(v_matrix, false, vmatrix);
    gl.uniformMatrix4fv(m_matrix, false, mmatrix);
    gl.uniformMatrix4fv(p_matrix, false, pmatrix);

    //开启隐藏面消除
    gl.enable(gl.DEPTH_TEST);
    mousecall();
    drawgl();
};
canvas.setAttribute('width', config.width);
canvas.setAttribute('height', config.height);
gl = Utils.GetWebGlContext(canvas);
// var param = window.location.search;
// var vid = param.substr(4);
// console.log(param);
// console.log(vid);
if (gl) {
    Utils.QueueImg([{
        "src": "./images/1.jpg"
    }], function(bitmaps) {
        video = bitmaps["src"];
        createpgl();
    });
    // createplay();
} else {
    console.log("开始webgl失败");
};

也可以通过PhotoSphereViewer来实现

<script type="text/javascript">

			var div = document.getElementById('container');

			var PSV = new PhotoSphereViewer({
				panorama: './images/snow.jpg',
				container: div,
				navbar: false,
				time_anim: 1000,
				anim_speed: '0.3rpm',
				size: {
					width: '100%',
					height: '800px'
				}
			});

	</script>

效果如下:

播放全景视频

也可以通过Server启动

var http = require('http');
var fs = require('fs');
var documentRoot = 'F:/work/picture/pictureView/WebServer'; //设置文件的根目录,可以修改为个人的自定义目录。
var server = http.createServer(function(req,res) {
    var url = req.url;
    var file = documentRoot + url;
    console.log(url);
    fs.readFile(file,function(err,data) {
        if(err){
            res.writeHeader(404,{
                'content-type':'text/html;charset="utf-8"'
            });
            res.write('<h1>404错误</h1><p>你要找的页面不存在</p>');
            res.end();
        }else{
            res.writeHeader(200,{
                'content-type':'text/html;charset=utf-8"'
            });
            res.write(data);
            res.end();
        }
    });
}).listen(8888);//设置的端口号,建议为6000以上。
console.log('服务器开启成功');

整个工程资源 打包在一起了:

猜你喜欢

转载自blog.csdn.net/Highning0007/article/details/129471054