WebGL的原生API不可能那么可爱

本文对比了使用Canvas2DContext、Three.js及WebGL三种方式绘制一个简单6*6像素点的方法,展示了不同技术手段的具体实现过程及其代码复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文简介

通过三种方法实现同一目标来展示WebGL原生API,并引导读者入门WebGL(伪)。

本文将分别使用Canvas 2D Context,Three.js,WebGL来绘制一个最简单的图形——一个6*6像素的点。

正文

当你第一次听说浏览器支持WebGL了,你可能欢呼雀跃,暗喜我大前端也有能操纵3D图形的一天。你按捺不住激动的心情拿出冰箱里百威啤酒,猛喝了一口。你潇洒的打开浏览器,Google了一下WebGL的教程,欣赏起了WebGL代码。可是就是你那惊鸿一瞥的刹那间,喷了屏幕一口泡沫/一口老血喷在屏幕上。“我的JavaScript怎么可能长这样?!”,你高声大喊道。是的,你没有看错,这货真不是JavaScript。这混合着attribute,gl_Position,precision等你从未在前端看到的词汇的怪物,到底是何方妖孽!你感到一阵眩晕,胃里也翻滚了起来,就好像你刚一口吞下了一份插着鸡毛的圣代……

而本文就是为了在你伤口上撒点盐,放点葱而存在的!

说到画一个点,大概谁都会。你熟练的从书包里掏出水彩笔,在白纸的正中央用力的点了一点,骄傲的喊道:“老师,我第一个画完了!”老师在你额头上贴了一朵小红花,周围掌声雷动,连绵不息。

但是如果不能用笔呢?我们先来看看如何用Canvas 2D Context画一个点。

Canvas 2D Context

这个比较简单,就不添加注释了,下面是代码。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <style>
        #cvs {
            border: 2px solid pink;
        }
    </style>
    <script>
        addEventListener("DOMContentLoaded", function () {
            var cvs = document.getElementById("cvs");
            var cxt = cvs.getContext("2d");
            var pointSize = 6;
            cxt.fillStyle = "rgba(0,0,0,1)";
            cxt.fillRect(0, 0, cvs.width, cvs.height);
            cxt.fillStyle = "rgba(77,153,204,1)";
            cxt.fillRect((cvs.width - pointSize) / 2, (cvs.height - pointSize) / 2, pointSize, pointSize);
        });
    </script>
</head>
<body>
    <canvas id="cvs" width="800" height="600"></canvas>
</body>
</html>

你掰开指头算了一下,刚好七个指头。是的,用这种方法画一个点只要7行代码而已。下面的截图应该和代码在你大脑里运行的结果一样。


黑色的画布里一个蓝点。

Three.js

接下来我们来看看Three.js如何实现在浏览器里画一个简单的点。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <style>
        #threejs {
            border: 2px solid pink;
            width:800px;
            height:600px;
        }
    </style>
    <script src="three.js"></script>
    <script>
        addEventListener("DOMContentLoaded", function () {
            var threejs = document.getElementById("threejs");
            //点的尺寸
            var size = 6;
            var width = 800, height = 600;
            //新建场景
            var scene = new THREE.Scene();
            //新建正射投影摄像机,摄像机的可视截面宽为width,高为height,近截面距离为0.1,远截面为1
            var camera = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, 0.1, 1);
            //摄像机往z轴移1,才能看到物体
            camera.position.z = 1;
            //新建渲染器
            var renderer = new THREE.WebGLRenderer();
            //设置渲染视口大小,你可以改成400,300试试
            renderer.setSize(width, height);
            //添加画布到文档中
            threejs.appendChild(renderer.domElement);
            //新建平面几何结构,宽高都为6
            var geometry = new THREE.PlaneGeometry(size, size);
            //新建材质,颜色为蓝色
            var material = new THREE.MeshBasicMaterial({ color: 0x4d99cc });
            //结合物体的结构和材质
            var plane = new THREE.Mesh(geometry, material);
            //添加到场景里才能被渲染
            scene.add(plane);
            //开始使用给定的摄像机渲染给定的场景
            renderer.render(scene, camera);
        });
    </script>
</head>
<body>
    <div id="threejs"></div>
</body>
</html>

你用上了脚趾头发现three.js花了14行代码来画一点简单的点。我们的WebGL选手能不能打破Three.js的记录并夺得最繁琐画点大赛的桂冠呢?

WebGL

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <style>
        #webgl {
            border: 2px solid pink;
        }
    </style>
    <script id="vertexShader" type="x-shader/x-vertex">
        //这部分是顶点着色器
        //声明一个attribute类型的4维向量a_Position
        attribute vec4 a_Position;
        void main()
        {
            //gl_Position和gl_PointSize是预定义变量,表示顶点位置和大小
            gl_Position = a_Position;
            gl_PointSize = 6.0;
        }
    </script>
    <script id="fragmentShader" type="x-shader/x-fragment">
        //这部分是片段着色器
        void main()
        {
            //gl_FragColor是预定义变量,表示片段的颜色
            gl_FragColor = vec4(0.3, 0.6, 0.8, 1.0);
        }
    </script>
    <script>
        addEventListener("DOMContentLoaded", function () {
            var webgl = document.getElementById("webgl");
            //获取3D上下文
            var gl = webgl.getContext("webgl");
            //获取顶点着色器的源代码
            var vertexGLSL = document.getElementById("vertexShader").text;
            //创建顶点着色器
            var vertexShader = gl.createShader(gl.VERTEX_SHADER);
            //设置顶点着色器的源代码
            gl.shaderSource(vertexShader, vertexGLSL);
            //编译顶点着色器
            gl.compileShader(vertexShader);
            //获取片段着色器的源代码
            var fragmentGLSL = document.getElementById("fragmentShader").text;
            //创建片段着色器
            var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
            //设置片段着色器的源代码
            gl.shaderSource(fragmentShader, fragmentGLSL);
            //编译片段着色器
            gl.compileShader(fragmentShader);
            //创建着色器程序
            var program = gl.createProgram();
            //程序加入顶点着色器
            gl.attachShader(program, vertexShader);
            //程序加入片段着色器
            gl.attachShader(program, fragmentShader);
            //链接程序
            gl.linkProgram(program);
            //启用程序
            gl.useProgram(program);
            //获取attribute类型变量a_Position在内存里的位置
            var a_Position = gl.getAttribLocation(program, "a_Position");
            //给a_Position赋值
            gl.vertexAttrib2f(a_Position, 0.0, 0.0);
            //将颜色缓冲区的值设置为黑色
            gl.clearColor(0, 0, 0, 1);
            //使用颜色缓冲区的值来填充
            gl.clear(gl.COLOR_BUFFER_BIT);
            //画点
            gl.drawArrays(gl.POINTS, 0, 1);
        });
    </script>
</head>
<body>
    <canvas id="webgl" width="800" height="600"></canvas>
</body>
</html>

你掰了一会儿指头,无奈的发现指头不够用了。最后你找到了一根绳子,通过打结发现一共有30行。无疑WebGL是夺冠了,你微笑着大声宣布出这个消息,并给WebGL戴上了草结的花圈……

反馈

如果你发现错误或者心存疑问,欢迎在下面留言。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值