WebGL学习(3) – 3D模型

  原文地址:WebGL学习(3) –
3D模型
  相信广大人口是以创逼真酷炫的老三维效果也目标要读书webGL的吧,首先自己哪怕是。我掌握了足足的webGL技巧后,正准备大展身手时,遇到了相同种骑虎难下的情景:还是开不起想使的东西。为底也,因为没3D模型可供应操作啊,纯粹用代码构建复杂的3D模型完全不可想像。那得下3dMax,maya,以及开源之blender等建模软件拓展构建。既然都入了webGL的坑了,那呢只好硬在头皮继续读书3D建模,断断续续学了一个几近月份之blender教程,总算入门了。
  这节主要学习怎么样导入模型文件,然后据此代码用效益,操作模型。首先展示下自家之绝唱,喷火战斗机的3D模型:webGL
喷火战斗机

语言 1

电脑行业提高十分抢,大学里的傅基本都跟不上实际的社会急需。如果你所于的学府还于指定大家利用谭浩强的教材,或下VC6.0来教大家上机实验,那若不妨看看本文,这里来一对建议可以帮助而无会见退出社会最为远。

内容大纲

  1. 型文件
  2. 着色器
  3. 光照
  4. 范变换
  5. 事件处理

考虑到这是一个恢复人感到挺基础,但是对新家又当辛辛苦苦的问题,我眷恋使当时篇稿子要想写得不吃你看专业名词扎堆,内容高深莫测,读了要不知所云,那无与伦比好的道或是,我们由此关系的措施拿全副「C语言应该怎么套」的题材由小与异常的拓展,慢慢来谈。

范文件

  blender导出之模子文件plane.obj,
同时还包材质文件plane.mtl。模型包括2800基本上单极,2200差不多单面,共200几近k的体积,内容比较充分,所以只好拿文件加载入html文件于便利。
  那怎么加载呢?一般会利用ajax获取,但自此出重新有益的艺术。那就是是用模型文件内容预编译直出至html中,这样不光提高了加载性能,开发也再便宜。具体而参照我之前的章:前者快速开模版
  这里运用自家之前的支出模版,
将模型(obj、mtl)文件为字符串的款式写入text/template模版中,同时以GLSL语言写的着色器也预编译到html中。到时用gulp的吩咐构建页面,所有内容就见面自动生成至页面中,html部分的代码如下所示:

    {% extends '../layout/layout.html' %}
    {% block title %}spitfire fighter{% endblock %}
    {% block js %}
    <script src="./lib/webgl.js"></script>
    <script src="./lib/objParse.js"></script>
    <script src="./lib/matrix.js"></script>
    <script src="./js/index.js"></script>
    {% endblock %}
    {% block content %}
    <div class="content">
    <p>上下左右方向键 调整视角,W/S/A/D键 旋转模型, +/-键 放大缩小</p>
    <canvas id="canvas" width="800" height="600"></canvas>
    </div>
    <!-- obj文件 -->
    <script type="text/template" id="tplObj">
    {% include '../model/plane.obj' %}
    </script>
    <!-- mtl文件 -->
    <script type="text/template" id="tplMtl">
    {% include '../model/plane.mtl' %}
    </script>
    <!-- 顶点着色器 -->
    <script type="x-shader/x-vertex" id="vs">
    {% include '../glsl/vs.glsl' %}
    </script>
    <!-- 片元着色器 -->
    <script type="x-shader/x-fragment" id="fs">
    {% include '../glsl/fs.glsl' %} 
    </script>
    {% endblock %}

每当始发前,我们先来举行一个有些测试。

obj文件

  obj文件包含的凡范的顶点法线索引等信息。这里以无限简便易行的立方体为条例。

  • v 几何体顶点
  • vt 贴图为标点
  • vn 顶点法线
  • f 面:顶点索引 / 纹理坐标索引 / 法线索引
  • usemtl 使用的质料名称

    # Blender v2.79 (sub 0) OBJ File: ''
    # www.blender.org
    mtllib cube.mtl
    o Cube
    v -0.442946 -1.000000 -1.000000
    v -0.442946 -1.000000 1.000000
    v -2.442946 -1.000000 1.000000
    v -2.442945 -1.000000 -1.000000
    v -0.442945 1.000000 -0.999999
    v -0.442946 1.000000 1.000001
    v -2.442946 1.000000 1.000000
    v -2.442945 1.000000 -1.000000
    vn 0.0000 -1.0000 0.0000
    vn 0.0000 1.0000 0.0000
    vn 1.0000 0.0000 0.0000
    vn -0.0000 -0.0000 1.0000
    vn -1.0000 -0.0000 -0.0000
    vn 0.0000 0.0000 -1.0000
    usemtl Material
    s off
    f 1//1 2//1 3//1 4//1
    f 5//2 8//2 7//2 6//2
    f 1//3 5//3 6//3 2//3
    f 2//4 6//4 7//4 3//4
    f 3//5 7//5 8//5 4//5
    f 5//6 1//6 4//6 8//6

相同、小测试,你准备好读书C语言了吧?

mtl文件

  mtl文件包含的是范的质料信息

  • Ka 环境色 rgb
  • Kd 漫反射色,材质颜色 rgb
  • Ks 高光色,材质高光颜色 rgb
  • Ns 反射高光 指定材质的反光指数
  • Ni 折射值 指定材质表面的就密度
  • d 透明度

    # Blender MTL File: 'None'
    # Material Count: 1

    newmtl Material
    Ns 96.078431
    Ka 1.000000 1.000000 1.000000
    Kd 0.640000 0.640000 0.640000
    Ks 0.500000 0.500000 0.500000
    Ke 0.000000 0.000000 0.000000
    Ni 1.000000
    d 1.000000
    illum 2

  知道了obj和mtl文件的格式,我们要做的尽管是读取它们,逐行分析,这里运用的objParse读取解析,想了解里面原理,可以查看源代码,这里不详述。
  提取出得的信后,就可将模型信息写副缓冲区,然后渲染出来。

    var canvas = document.getElementById('canvas'),
        gl = get3DContext(canvas, true),
        objElem = document.getElementById('tplObj'),
        mtlElem = document.getElementById('tplMtl');
    function main() {
        //...

        //获取变量地址
        var program = gl.program;
        program.a_Position = gl.getAttribLocation(gl.program, 'a_Position');
        //...

        // 创建空数据缓冲
        var vertexBuffer = createEmptyArrayBuffer(gl, program.a_Position, 3, gl.FLOAT);
        //...

        // 分析模型字符串
        var objDoc = new OBJDoc('plane',objElem.text,mtlElem.text);
        if(!objDoc.parse(1, false)){return;}
        var drawingInfo = objDoc.getDrawingInfo();

        // 将数据写入缓冲区
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, drawingInfo.vertices, gl.STATIC_DRAW);
        //...
    }

而是不是听说过二上制数,他们是怎进行演算的?(基本的累累之进制知识)

着色器

卿会说有一个极端小之微机体系由什么有构成为?(基本的处理器结构知识)

极端着色器

  顶点着色器比较简单,和前的区别比较深之是,把计算颜色光照部分更换到了片元着色器,这样可以实现逐片元光照,效果会越来越栩栩如生和当。

    attribute vec4 a_Position;//顶点位置
    attribute vec4 a_Color;//顶点颜色
    attribute vec4 a_Scolor;//顶点高光颜色
    attribute vec4 a_Normal;//法向量
    uniform mat4 u_MvpMatrix;//mvp矩阵
    uniform mat4 u_ModelMatrix;//模型矩阵
    uniform mat4 u_NormalMatrix;
    varying vec4 v_Color;
    varying vec4 v_Scolor;
    varying vec3 v_Normal;
    varying vec3 v_Position;

    void main() {
        gl_Position = u_MvpMatrix * a_Position;
        // 计算顶点在世界坐标系的位置
        v_Position = vec3(u_ModelMatrix * a_Position);
        // 计算变换后的法向量并归一化
        v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));
        v_Color = a_Color;
        v_Scolor = a_Scolor;
    }

微机是什么存储图像的? (数字化原理)

光照

  光照相关的计主要以片元着色器中,首先科普一下光照的连锁信息。

    物体呈现出颜色亮度就是表面的反射光导致,计算反射光公式如下:
    <表面的反射光颜色> = <漫反射光颜色> + <环境反射光颜色> + <镜面反射光颜色>

    1. 其中漫反射公式如下:
    <漫反射光颜色> = <入射光颜色> * <表面基底色> * <光线入射角度>

    光线入射角度可以由光线方向和表面的法线进行点积求得:
    <光线入射角度> = <光线方向> * <法线方向>

    最后的漫反射公式如下:
    <漫反射光颜色> = <入射光颜色> * <表面基底色> * (<光线方向> * <法线方向>)

    2. 环境反射光颜色根据如下公式得到:
    <环境反射光颜色> = <入射光颜色> * <表面基底色>

    3. 镜面(高光)反射光颜色公式,这里使用的是冯氏反射原理
    <镜面反射光颜色> = <高光颜色> * <镜面反射亮度权重> 

    其中镜面反射亮度权重又如下
    <镜面反射亮度权重> = (<观察方向的单位向量> * <入射光反射方向>) ^ 光泽度

上面就三独问题,能回复的同室举手。如果您举手了,那么我们上下路的座谈,否则,我用报您,你现在极其紧要的事务,是不久找一据《计算机科学导论》把准备知识互补好。

片元着色器

  着色器代码就是指向端公式内容之演绎

    #ifdef GL_ES
    precision mediump float;
    #endif
    uniform vec3 u_LightPosition;//光源位置
    uniform vec3 u_diffuseColor;//漫反射光颜色
    uniform vec3 u_AmbientColor;//环境光颜色
    uniform vec3 u_specularColor;//镜面反射光颜色
    uniform float u_MaterialShininess;// 镜面反射光泽度
    varying vec3 v_Normal;//法向量
    varying vec3 v_Position;//顶点位置
    varying vec4 v_Color;//顶点颜色
    varying vec4 v_Scolor;//顶点高光颜色

    void main() {
        // 对法线归一化
        vec3 normal = normalize(v_Normal);
        // 计算光线方向(光源位置-顶点位置)并归一化
        vec3 lightDirection = normalize(u_LightPosition - v_Position);
        // 计算光线方向和法向量点积
        float nDotL = max(dot(lightDirection, normal), 0.0);
        // 漫反射光亮度
        vec3 diffuse = u_diffuseColor  * nDotL * v_Color.rgb;
        // 环境光亮度
        vec3 ambient = u_AmbientColor * v_Color.rgb;
        // 观察方向的单位向量V
        vec3 eyeDirection = normalize(-v_Position);
        // 反射方向
        vec3 reflectionDirection = reflect(-lightDirection, normal);
        // 镜面反射亮度权重
        float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), u_MaterialShininess);
        // 镜面高光亮度
        vec3 specular =  v_Scolor.rgb * specularLightWeighting ;
        gl_FragColor = vec4(ambient + diffuse + specular, v_Color.a);
    }

因为——

型变换

  这里先安装光照相关的上马标准,然后是mvp矩阵变换和法向量矩阵相关的计算,具体知识点可参照前的章*[WebGL学习(2)

  • 3D场景](http://www.cnblogs.com/edwardloveyou/p/7833639.html)*

  要留意的是逆转置矩阵,主要用以计算模型变换之后的法向量,有矣变后的法向量才能够是计算光照。

     求逆转置矩阵步骤
        1.求原模型矩阵的逆矩阵
        2.将逆矩阵转置

    <变换后法向量> = <逆转置矩阵> * <变换前法向量>

  给正在色器变量赋值然后绘制出模型,最后调用requestAnimationFrame不断执行动画。矩阵的旋部分可组成下面的keydown事件进行查看。

    function main() {
        //...

        // 光线方向
        gl.uniform3f(u_LightPosition, 0.0, 2.0, 12.0);
        // 漫反射光照颜色
        gl.uniform3f(u_diffuseColor, 1.0, 1.0, 1.0);
        // 设置环境光颜色
        gl.uniform3f(u_AmbientColor, 0.5, 0.5, 0.5);
        // 镜面反射光泽度
        gl.uniform1f(u_MaterialShininess, 30.0);

        var modelMatrix = new Matrix4();
        var mvpMatrix = new Matrix4();
        var normalMatrix = new Matrix4();
        var n = drawingInfo.indices.length;

        (function animate() {
            // 模型矩阵
            if(notMan){ angleY+=0.5; }
            modelMatrix.setRotate(angleY % 360, 0, 1, 0); // 绕y轴旋转
            modelMatrix.rotate(angleX % 360, 1, 0, 0); // 绕x轴旋转

            var eyeY=viewLEN*Math.sin(viewAngleY*Math.PI/180),
                len=viewLEN*Math.cos(viewAngleY*Math.PI/180),
                eyeX=len*Math.sin(viewAngleX*Math.PI/180),
                eyeZ=len*Math.cos(viewAngleX*Math.PI/180);

            // 视点投影
            mvpMatrix.setPerspective(30, canvas.width / canvas.height, 1, 300);
            mvpMatrix.lookAt(eyeX, eyeY, eyeZ, 0, 0, 0, 0, (viewAngleY>90||viewAngleY<-90)?-1:1, 0);
            mvpMatrix.multiply(modelMatrix);
            // 根据模型矩阵计算用来变换法向量的矩阵
            normalMatrix.setInverseOf(modelMatrix);
            normalMatrix.transpose();

            // 模型矩阵
            gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
            // mvp矩阵
            gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
            // 法向量矩阵
            gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);

            // 清屏|清深度缓冲
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            // 根据顶点索引绘制图形(图形类型,绘制顶点个数,顶点索引数据类型,顶点索引中开始绘制的位置)
            gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
            requestAnimationFrame(animate);
        }());
    }

C语言说到底是千篇一律山头为内存呢着力的编程语言,你能够免能够效仿懂她,其实特别挺程度上无是在你智商高低,而是你是不是具扎实的计算机结构、存储、运算原理方面的知!!

事件处理

  +/-
键实现放大/缩小场景的效益;WSAD键实现模型的团团转,也不怕是兑现绕x轴和y轴旋转;上下左右方向键实现之凡视点的旋。矩阵变换的连带落实参考上面代码的动画部分。

  模型旋转和视点旋转看在特别相似,其实以发出两样的。视点的团团转是整整场景比如光照模型等还是随后变动之,如果因场景做参照物,它便相当给口变更观察位置看到物体。而模型旋转也,它就旋转模型自身,外部的日照和状况都是不更换的,以场景做参照物,相当给口当平职务来看模型在动。从demo的光照好看来两种方法的别。

    document.addEventListener('keydown',function(e){
        if([37,38,39,65,58,83,87,40].indexOf(e.keyCode)>-1){
            notMan=false;
        }
        switch(e.keyCode){
            case 38:        //up
                viewAngleY-=2;
                if(viewAngleY<-270){
                    viewAngleY+=360
                }
                break;
            case 40:        //down
                viewAngleY+=2;
                if(viewAngleY>270){
                    viewAngleY-=360
                }
                break;
            case 37:        //left
                viewAngleX+=2;
                break;
            case 39:        //right
                viewAngleX-=2;
                break;
            case 87:        //w
                angleX-=2;
                break;
            case 83:        //s
                angleX+=2;
                break;
            case 65:        //a
                angleY+=2;
                break;
            case 68:        //d
                angleY-=2;
                break;
            case 187:       //zoom in
                if(viewLEN>6) viewLEN--;
                break;
            case 189:       //zoom out
                if(viewLEN<30) viewLEN++;
                break;
            default:break;
        }
    },false);

于这边,我思念强调,任何新知识的习都是生一定之前提条件的。C语言学习的前提条件就是,对电脑体系如有一个完完全全的,科学的基本功认识。
脱离了此基础认识,一切还死困难。

总结

  最后,个人感觉建立3D模型或挺费时间,需要花心机慢慢调整,才会做出比较完善的型。

故,有些同学学不见面C语言,不是因她们智商挺,而是因为他俩从没未雨绸缪好。

我花费时写这篇稿子来深受大家讲C语言的求学,当然是盼大家真的的学会、学懂C语言,并能够真正感到到其的用途,所以老对不起我无见面如造机构那样,告诉您“零基础”就能开模拟。但一旦你真按照自说之错过做,那么至少从现在起,你实在是始有些“学院派”的认真了。

每当就首文章的最终,我引进了一致仍自己就读了的《计算机是导论》。这仍开可以说凡是自个儿的启蒙书,我念了,真心觉得好,所以推举给得的同桌。

好了,接下去我们进C语言的读书过程。

深受大家分享一个C/C++学习交流群:553014383 进博邀请码(编号):寂静

群内不定时享受干货,包括2017尽新的C/C++企业案例上材料以及零基础入门教程,欢迎自学的小白和大神入群学习交流。

亚、学C语言到底学些什么—— 「语法」和「函数库」

C语言学习的重大,是要是先来懂,学C语言到底是当模拟几什么?

本人如此一问,有的同学将翻开课本,指在目录说,我理解自家理解,有变量,数据类型,循环语句,函数,哦哦哦,还有指针等等!

死不满,如果你觉得读书C语言就是习这些东西,那你得快改正一下理念,否则连下的读会困难重重。因为我早已就是这般少至坑里之,这种狭隘的见让自己浪费了大量的时间去上琐碎的细节,又受我迟迟未可知接触到又重要的学识,结果是眼睛高手低——知道森人家不明白的失效知识,但是还要写不产生什么真正像样的次来。

所谓「语法」,就是入门教材里最要讲解的内容。也尽管是那些所谓的变量、数据类型、分支判断、循环、函数、指针等等。

这些情节比较平淡,但是好信息是这些内容并无麻烦,都是一些格式化的物。只要你差不多练习,就会自行的雕刻在你的脑力里,成为平等种下意识的习惯。


「语法」本身其实没什么用。因为它才是一模一样种植格式规范,你拟得还好,也无可知引导而写起了得的主次。因为以软件设计中,实际上不过核心的有些或在其
「函数库」部分。

什么是 「函数库」?

简的话,函数库就是他人修好之C函数,直接提供给您用,你要调用内的函数,就能兑现自然的功用。例如
printf()
函数,你必懂得,只要调用这个函数,你便能够当十分黑糊糊的窗口里亮平段文字。你连无了解
printf()
的中间工作原理,但是若知道乃要遵循说明去调用,就能落实对应之效力。

当时虽是函数库——别人写好的,打包送及您眼前,你得肆意调用来做各种各样的作业的函数集合。

我的话几独自函数库底函数,例如 CreateWindowEx()
函数可以用来创造一个窗体(这个函数由微软提供),例如 GaussianBlur()
函数可以为此来针对一个图像进行高斯歪曲处理(这个函数由 OpenCV 提供),再按照
evhttp_new语言() 函数可以创建一个 HTTP 服务程序(这个函数由 libevent
提供)……

任何还有什么库函数?太多了。从控制网络通信,到截取视频镜头并分析内部的口脸位置,到加解密本地文件,甚至包括微信收发信息,抓取淘宝商品信息……库函数的多寡以及中程度远远超过大家的想象。

诶?你根本不曾听说过还有这些事物?课本里啊未尝涉及?

那么是为您看之是入门教材,着重讲解语法,顺带涉及了少量的 C
语言自带的库函数而已。事实上 C
语言包含的库函数本身便时有发生为数不少,但是更多又强大的或者广大老三正库函数,例如我点提到的这些。

重要是在于,我想报您,库函数才是您念C语言并以其下被实际的重大!!

学会调用别人的库函数,甚至写起好的库函数,都是极其重要的。因为一个函数,本质上即是一个作用单位。你有所的根基设备越来越多,你的发挥空间越发怪。道理就是如此简单。这就算是自己急需往大家强调的老二单意见,要惦记写来实用的C程序,一杀重要就研究并学会用各种库函数。

卿见隔壁王二描绘了只三维程序能够于一个绚丽多姿的立方体在半空中打转?快找 OpenGL
库函数来用。什么?刘大宝写了只网络软件能于局域网里聊天?快找 Socket
库来用。

见了啊?库函数有差不多要?

然而,函数库底修并无是孤立的。许多库函数背后用肯定之天地知识支撑。同样如果自先是独意所述,需要准备知识。学懂一个函数库,代表的不只是懂如何调用那么粗略,
而更体现了咱们针对一个一定领域——网络、数字图像、密码学、操作系统等的认识。

其三、基本的上过程及一般规律

假定本人前所陈述,C语言学习重大是为语法入门,然后至函数库。再具体有些来说,包括以下流:

修基础语法

学学简单的个别几只C语言自带的函数

读书有顺序设计之基础知识(数据结构,算法)

读还多更强硬的C语言自带的函数

习一些一定应用领域的基本理论知识(操作系统,数据库,网络,图像……)

动用及同一阶段学习之顶文化愈学习其他人提供的函数库(网络拍卖、操作系统管理、图像、密码学等等)

对此本科阶段的同校来说,着重学好1-4,有取舍的上一下5-6,做片多少作品出来,就曾经好可怜不易了。这就算是相似的学习规律。说得要命简短,但是要到位真正十分不爱。

季、参考书籍

第一第一仍,也是我觉得最好要紧之书,是当下本《计算机对导论》。亚马逊地址:《计算机对导论(原书第2本)》
佛罗赞 (Behrouz A.Forouzan), 莫沙拉夫 (Firouz Mosharraf), 刘艺【摘要
书评 试读】图书

得说,这本书是针对自己人生影响无与伦比特别之一定量本书有。这仍开通俗易懂,知识面广,但还要有一定之深度。真是因为读了当时仍开,才为我起了针对性电脑科学合理的基本认识。在无关乎太多之数学原理的状况下,这本开清晰的公布了席卷电脑的中心组成,CPU运算的法则,内存、硬盘、光盘是何许存储数据的,编程的基本原理(机器语言、汇编语言,结构化编程语言,面向对象编程语言),以及台网、数据压缩等。

说白了,就是平等依照科普书。但是是平按照伟大之科普书。入门者的福音书。

接下来,第二仍,就是有关C语言语法的书本了,说实话这部份本身就没有印象看之啊开了。因为自此人口发只毛病,就是爱东翻翻,西翻翻,哪里不知情就四处找开。可见此片段其实没大家想象的那一定,你而找到同样如约好看得亮的题,就是好书!不必太过度拘泥于书跟书里的优劣比较。

无限经典的C语言入门书籍许多丁定要说凡是 K&R
的那依。但是事实上这按照开对多数新家的话要略微难,如果产生趣味呢足以读一念。但自我更建议你打探有C语言之后又变更过来看看会重新好。

柴田望洋的《明解C语言》最近凡相同依照广受欢迎的挚之C语言入门书。《明解C语言》
柴田望洋, 管杰, 罗勇【摘要 书评 试读】图书

昨日本人特别到书店翻了瞬间,这本开真的挺不错,亲切,谦虚,易懂。但我想呢未必适合所有人。如果您是冀学曲线和一些的,可以考虑看看。

哎呀,差点忘了,还有一样随咱们国内的赵岩先生写的《C语言点滴》也起是的反应,也许大家吧得以找寻来看望。《C语言点滴》
赵岩【摘要 书评 试读】图书

大抵,上面这些开便能帮助而于轻松的入门了。其他书籍推荐,我会陆续也勾勒出来。今天实在写不了这么多矣。

然而,再添相同论,如果对那些已经拟得差不多的同窗,必须得看下面就本,这是自己之至爱之一啊!^_^
那即便是挂炸天的《C Standard
Library》,china-pub链接:C标准库(C标准库“圣经”)[按需印刷]

精看看这本开,它是实在的一等教材,因为——里面富含了标准库实现的整源码,以及作者以贯彻时之笔触讲解。是一样准值得珍藏之特等力作。让咱一致见顶级程序员的考虑杰作。(吐嘈:这按照开之翻或发生一对值得修正的地方,所以会结英文版的同班便直接收英文版了)

立即几乎本书其实并未涵盖C语言学习之兼具重点点。有趣味的恋人我会还择文另外分享,就非累了。

五、晋级——通向更胜境界的路

程序员的路,学无止境。更规范的上至此其实才刚刚开始。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图