Babylon.js引擎(二)

三、组件

(4)光源

引擎自带有多种光源,现示例如下:

点光源示例,核心代码如下:

javascript 复制代码
1    var createScene = function () {                          //创建场景的方法
        2        var scene = new BABYLON.Scene(engine);               //获取场景对象
        3        var camera = new BABYLON.ArcRotateCamera("camera1",
        4             0, 0, 0, new BABYLON.Vector3(0, 0, 0), scene);  //创建弧度旋转相机
        5        camera.setPosition(new BABYLON.Vector3(0,30,35));   //设置相机的位置
        6        camera.attachControl(canvas, true);                  //开启控制
        7        //创建点光源并指定位置
        8        var light = new BABYLON.PointLight("light", new BABYLON.Vector3(-20, 20, 20),
                  scene);
        9        light.diffuse=new BABYLON.Color3(1, Math.random(), Math.random());
        10       light.range=100;                                     //设置光源的照射范围
        11       light.intensity=1.1;                                //设置光源的强度
        12       ......//此处省略了向场景中添加网格对象的方法,读者可自行查看随书源代码
        13       return scene;                                        //返回场景对象
        14   };

说明:此段代码的功能为创建场景和弧度旋转相机,设置相机位置并开启控制,创建点光源对象并指定点光源的位置、颜色、照射范围和光照强度。其中向场景添加网格对象的方法与前面案例中的网格对象代码相同,读者可自行查看随书源代码。

聚光灯光源示例,核心代码:

javascript 复制代码
 var createScene = function () {                          //创建场景
        2         var ambientColor=new BABYLON.Color3(0.2,0.2,0.2);  //地板环境色
        3         var scene = new BABYLON.Scene(engine);              //设置环境色
        4         scene.ambientColor=new BABYLON.Color3(1,1,1);      //设置场景环境色
        5         var camera = new BABYLON.ArcRotateCamera("Camera",
        6              0, 0.8, 90, BABYLON.Vector3.Zero(), scene);    //创建弧度旋转相机
        7         camera.lowerBetaLimit = 0.1;                        //设置相机最小的旋转角度
        8         camera.upperBetaLimit = (Math.PI / 2) * 0.9;       //设置相机最大的旋转角度
        9         camera.lowerRadiusLimit = 1;                        //设置相机最小的旋转半径
        10        camera.upperRadiusLimit = 150;                      //设置相机最大的旋转半径
        11        camera.attachControl(canvas, true);                 //开启控制
        12        camera.setPosition(new BABYLON.Vector3(-20, 11, -20)); //设置摄像机位置
        13        var light = new BABYLON.SpotLight("spotLight", new BABYLON.Vector3(-40, 40, -40),
        14             new BABYLON.Vector3(1, -1, 1), Math.PI / 5, 30, scene); //创建聚光灯光源
        15        light.position = new BABYLON.Vector3(-40, 40, -40);        //设置聚光灯位置
        16        light.shadowMaxZ = 100;                            //设置阴影投射的最远距离
        17        light.shadowMinZ = 10;                             //设置阴影投射的最近距离
        18        var shadowGenerator = new BABYLON.ShadowGenerator(1024, light); //创建阴影计算对象
        19        shadowGenerator.bias = 0.001;                      //设置阴影偏移量
        20        shadowGenerator.normalBias = 0.02;                 //设置正常偏移量
        21        shadowGenerator.useContactHardeningShadow = true; //开启接触硬化阴影
        22        shadowGenerator.contactHardeningLightSizeUVRatio = 0.05; //设置阴影的软化速度
        23        shadowGenerator.setDarkness(0.5);                  //设置暗值
        24        var meshArray=[];                                   //网格对象数组
        25        ......//此处省略了创建网格对象的代码,读者可自行查看随书源代码
        26        var groundMaterial = new BABYLON.StandardMaterial("groundMaterial", scene);
                  //创建地板的材质
        27        groundMaterial.ambientColor=ambientColor;         //设置地板环境色
        28        var ground =BABYLON.MeshBuilder.CreateGround("gd", {width: 60, height:60 ,
                  subdivsions: 4}, scene);
29        ground.material=groundMaterial;                    //设置地板材质
        30        ground.receiveShadows = true;                      //设置地板接收阴影
        31        var meshMaterial=new BABYLON.StandardMaterial("meshMaterial", scene); //创建材质
        32        meshMaterial.ambientColor=ambientColor;            //设置材质的环境色
        33        for(var i=0; i<meshArray.length; i++){               //遍历网格对象数组
        34             shadowGenerator.getShadowMap().renderList.push(meshArray[7-i]);
        35             meshArray[i].material=meshMaterial;            //设置各个网格对象的材质
        36             meshArray[i].material.diffuseColor=new BABYLON.Color3(1, Math.random(),
                      Math.random());
        37             meshArray[i].position.x=-16+Math.floor(i/2)*10; //设置各个网格对象的 x坐标
        38             meshArray[i].position.y=4;                       //设置各个网格对象的 y坐标
        39             meshArray[i].position.z=(i%2==0)? -4:6;          //设置各个网格对象的 z坐标
        40        }}

、 ❑ 第1~12行代码的功能为创建场景并设置场景的环境色,创建弧度旋转相机并设相机的最小旋转角度、最大旋转角度、最小旋转半径、最大旋转半径和位置以及开启相机控制。

❑ 第13~23行的功能为创建聚光灯光源并设置聚光灯光源的阴影投射范围,创建阴影计算对象并设置阴影偏移量、正常偏移量、阴影类型和暗值。其中,接触硬化阴影仅限WebGL 2.0中有。

❑ 第24~39行代码的功能为创建地板网格对象并设置其材质与位置,创建不同形状的网格对象并设置材质和位置,以及将它们放入阴影计算列表。

平行光源示例,核心代码 :

平行光源,示例如下:

javascript 复制代码
QQQ:
1    var createScene = function () {
        2        var scene = new BABYLON.Scene(engine);               //创建场景
        3        var camera = new BABYLON.ArcRotateCamera("camera1",
        4              0, 0, 0, new BABYLON.Vector3(0, 0, 0), scene); //创建弧度旋转摄像机
        5        camera.setPosition(new BABYLON.Vector3(0,30,35));   //设置弧度旋转相机的位置
        6        camera.attachControl(canvas, true);                  //开启控制
        7        var light = new BABYLON.DirectionalLight("DirectionalLight",
        8             new BABYLON.Vector3(-1, -1, -1), scene);        //创建平行光并指定方向
        9        light.diffuse=new BABYLON.Color3(1, Math.random(), Math.random());
                  //设置光的颜色
        10       light.position=new BABYLON.Vector3(30,30,30);       //设置阴影投放的位置
        11       light.shadowMaxZ = 100;                              //设置最大的投影距离
        12       light.shadowMinZ = 10;                     //设置最小的投影距离
        13       ......//此处省略了阴影计算和创建网格对象的代码,读者可自行查看随书源代码
        14       return scene;                              //返回场景对象
        15       };

此段代码的功能为创建场景对象和弧度旋转摄像机,并设置其位置以及开启相机控制,创建平行光并指定其方向、阴影投放位置和投影范围。创建平行光中的第二个参数为光源照射的方向。此外,需特别注意光源的position属性,其设置不当可能导致部分网格对象无法投射出阴影。

(5) 材质

对网格对象进行材质设置,网格对象才能更美观地呈现在场景中。材质决定了此网格的颜色、透明度等外观信息。

1.标准材质

标准材质允许在网格对象上覆盖颜色和纹理,并且需要光线才能看,

示例搭建基本场景,创建4个球体网格对象和标准材质,核心代码如下:

javascript 复制代码
1     var canvas = document.getElementById("renderCanvas");   //获取Canvas DOM对象
        2     var engine = new BABYLON.Engine(canvas, true);          //获取Babylon引擎对象
        3     var createScene = function () {
        4         var scene = new BABYLON.Scene(engine);               //获取场景对象
        5         var camera = new BABYLON.ArcRotateCamera("Camera",
        6               -Math.PI / 2, 3 * Math.PI / 16, 15, BABYLON.Vector3.Zero(), scene);
                        //创建弧度旋转相机
        7         camera.attachControl(canvas, true);                  //开启相机控制
        8         var redMat = new BABYLON.StandardMaterial("redMat", scene);  //创建标准材质
        9         redMat.emissiveColor = new BABYLON.Color3(1, 0, 0); //设置自发光颜色为红色
        10        ......//此处省略创建其他球体标准材质的代码,读者可自行查看随书源代码
        11        var sphereCenter=BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 0.0
                  1}, scene);
        12        var lightRed = new BABYLON.SpotLight("spotLight",
        13             new BABYLON.Vector3(), new BABYLON.Vector3(0, -1, 0), Math.PI / 2, 1.5,
                      scene);
        14        lightRed.diffuse = new BABYLON.Color3(1, 0, 0);    //设置聚光灯颜色为红色
        15        lightRed.position.set(3,2,3);                       //设置红色聚光灯的位置
        16        lightRed.parent=sphereCenter;                       //将其父类属性指向中心球
        17        ......//此处省略创建其他颜色聚光灯光源的代码,读者可自行查看随书源代码
        18        var redSphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter:
                  0.25}, scene);
        19        redSphere.material = redMat;                     //设置其材质
        20        redSphere.position = lightRed.position;         //球体位置设为红色聚光灯的位置
        21        redSphere.parent=sphereCenter;                   //将其父类属性指向中心球
        22        ......//此处省略创建其他颜色球体网格对象的代码,读者可自行查看随书源代码
        23        var groundMat = new BABYLON.StandardMaterial("groundMat", scene);
                  //创建地板的标准材质
        24        var ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 15, height
                  : 15}, scene);
        25        ground.material = groundMat;                     //设置地板的材质
        26        var angle=0;                                      //旋转角度
        27        scene.onBeforeRenderObservable.add(()=>{        //场景渲染之前的执行函数
        28             angle=angle+0.01;                            //角度增加
        29             sphereCenter.rotation.set(0, angle,0);
        30        ......//此处省略Babylon中GUI界面的搭建和控制方法的代码,它将在下文进行详细介绍
        31    }

❑ 第1~6行的功能为获取Canvas DOM对象、Babylon引擎对象、场景对象,创建弧度旋转相机,开启相机控制。

❑ 第7~25行为创建4种标准材质并设置为自发光颜色。创建4种不同颜色的聚光灯光源,创建4种不同颜色的球体网格对象,将球体位置设置为对应颜色聚光灯光源的位置,将聚光灯和球体的父类属性都指向中心球,最后创建地板并设置其材质为标准材质。

❑ 第26~31行代码为不断旋转中心球,从而实现旋转聚光灯光源和球体网格对象。将聚光灯和球体的父类属性都指向中心球,此时中心球中包含聚光灯和球体。旋转中心球,就会同时旋转聚光灯光源和球体网格对象,读者可以将这个作为一个开发技巧积累下来。

示例中GUI界面的开发和控制方法的开发。具体代码如下。

javascript 复制代码
1     var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
              //全屏纹理
        2     var sceneAmbientColorFlag=false;                            //场景环境色开启标志
        3     var leftPanel= createPanel(advancedTexture,
        4         BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER,
        5              BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT);    //左侧面板
        6     var senceCheckBox=createCheckbox(leftPanel, "开启场景环境颜色",
        7         BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT);        //创建单选框
        8     senceCheckBox.onIsCheckedChangedObservable.add(function (value){//单选框控制方法
        9         sceneAmbientColorFlag=value;                          //更改场景环境色开启标志
        10        if(! sceneAmbientColorFlag) {                          //如果没开启
        11             scene.ambientColor=new BABYLON.Color3(0,0,0); }}) //场景环境色设置为黑色
        12    var senceColorPicker=createColorPicker(leftPanel, false, "",
        13        scene.ambientColor, BABYLON.GUI.Control.HORIZONTAL_ALIGNMENfuT_CENTER);
        14    senceColorPicker.onValueChangedObservable.add(function(value) {  //颜色改变事件
        15        if(sceneAmbientColorFlag) {                     //如果开启场景环境色
        16             scene.ambientColor.copyFrom(value); }});    //将场景环境色指定为选择的颜色值
        17    ......//此处省略其他颜色选择器的创建和控制方法的代码,读者可自行查看随书源代码
        18    function createCheckbox(panel, text, horizontalAlignment) {    //创建单选框
        19        var checkbox = new BABYLON.GUI.Checkbox();                //新建单选框
        20        checkbox.width = "20px";                                   //单选框的宽
        21        checkbox.height = "20px";                                 //单选框的高
        22        checkbox.isChecked = false;                               //单选框的状态
        23        checkbox.color = "green";                                 //单选框的颜色
        24        var header = BABYLON.GUI.Control.AddHeader(checkbox, text, "180px", {
                  //给单选框添加文字头
        25             isHorizontal: true,
        26             controlFirst: true});
        27        header.height = "30px";                          //设置文字头的高度
        28        header.color = "white";                          //设置文字的颜色
        29        header.outlineWidth ="4px";                      //轮廓宽度
        30        header.outlineColor ="black";                    //轮廓颜色
        31        header.horizontalAlignment=horizontalAlignment; //水平布局
        32        panel.addControl(header);               //将文字头添加进面板
        33        return checkbox; }                                //返回单选框对象
        34    function createPanel(advancedTexture, verticalAlignment, horizontalAlignment) {
              //创建面板
        35        var panel = new BABYLON.GUI.StackPanel();       //创建静态的面板
        36        panel.width = "200px";                           //面板的宽
        37        panel.verticalAlignment = verticalAlignment;    //面板的垂直布局
        38        panel.horizontalAlignment = horizontalAlignment; //面板的水平布局
        39        advancedTexture.addControl(panel);               //添加控制
        40        return panel; }                                    //返回面板
        41    function createColorPicker(panel, usetext, text, defultColor, horizontalAlignment)
              {//创建颜色控件
        42        if(usetext){                                      //如果使用文本控件
        43             var textBlock = new BABYLON.GUI.TextBlock(); //创建文本控件
        44             textBlock.text = text;                       //设置文本内容
        45             textBlock.color = "white";                   //设置文本颜色
        46             textBlock.height = "30px";                   //文本颜色
        47             panel.addControl(textBlock); }               //面板中添加文本控件
        48        var picker = new BABYLON.GUI.ColorPicker();     //创建颜色控件
        49        picker.value =defultColor;                       //默认颜色
        50        picker.height = "150px";                         //颜色控件的高
        51        picker.width = "150px";                          //颜色控件的宽
        52        picker.horizontalAlignment =horizontalAlignment; //水平布局方式
        53        panel.addControl(picker)                         //添加控制
        54        return picker; }                                   //返回颜色控件

❑ 第1~18行的功能为创建GUI全屏纹理、左右侧面板、单选框和颜色选择器,其中单选框添加状态改变事件,颜色选择器添加状态改变事件。单选框控制场景环境色的开启,颜色选择器会改变地板材质的环境颜色、漫反射颜色、自发光颜色和镜面颜色。

❑ 第18~33行为创建GUI单选框,设置单选框的宽度、高度、状态、颜色,并添加文字头说明单选框的作用。然后设置文字头的高度、文字颜色、轮廓宽度、轮廓颜色和水平布局方式,最后将文字头添加进面板和返回单选框对象。

❑ 第34~40行为创建面板,设置面板的宽度,垂直布局和水平布局方式,并添加进GUI全屏纹理,最后返回面板对象。

❑ 第41~54行为创建颜色控件,添加文本控件以及设置文本控件的内容、颜色和文本的高度,设置颜色控件的宽度、高度和水平布局方式,最后将颜色控件添加进面板并返回面板对象。

2.着色器材质

它允许开发人员使用自定义的着色器进行渲染。通过该材质可以开发出很多酷炫的特效,对画面的提升有巨大作用。

示例中创建着色器材质,将着色器材质应用到圆环结网格对象,具体代码如下所示:

javascript 复制代码
1     var createScene = function() {                       //创建场景的函数
        2         var scene = new BABYLON.Scene(engine);          //创建场景
        3               var camera = new BABYLON.ArcRotateCamera("Camera",
        4              0, Math.PI / 2, 12, BABYLON.Vector3.Zero(), scene); //创建弧度旋转摄像机
        5         camera.attachControl(canvas, false);             //设置摄像机的控制
        6         ......//此处省略顶点着色器和片元着色器的代码,它们将在下文进行详细介绍
        7         var shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, {//创建着色器材质
        8              vertex: "custom",                            //着色器传入方式
        9              fragment: "custom", }, {
        10                 attributes: ["position", "normal"],     //attribute变量
        11                 uniforms: ["world", "worldView", "worldViewProjection", "view",
                          "projection"]});
        12        var refTexture = new BABYLON.Texture("textures/ref.jpg", scene);
                  //加载计算所需纹理
        13        refTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;  //设置 U轴纹理拉伸方式
        14        refTexture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;  //设置 V轴纹理拉伸方式
        15        shaderMaterial.setTexture("refSampler", refTexture);   //将纹理送入着色器
        16        shaderMaterial.backFaceCulling = false;                 //材质关闭背面剪裁
        17        var mesh = BABYLON.Mesh.CreateTorusKnot("mesh", 2, 0.5, 128, 64, 2, 3, scene);
                  //创建圆环结
        18        mesh.material = shaderMaterial;                 //将圆环结的材质设置为着色器材质
        19        var angle=0;                                     //旋转角度
        20        scene.onBeforeRenderObservable.add(()=>{       //渲染之前对应的函数
        21             angle=angle+0.01;                           //增加旋转角度
        22             mesh.rotation.set(0, angle,0);               //修改圆环结的旋转角度
        23        })
        24        return scene;                                    //返回场景
        25    }

此段代码的作用主要是创建场景和弧度旋转摄像机,然后创建着色器材质,并且指定着色器传入方式,传入attribute变量和uniform变量。然后加载纹理,设置纹理UV轴的拉伸方式,关闭背面剪裁并将纹理送入GPU管线。最后将圆环结的材质设置为着色器材质,并不断旋转圆环结。本案例采用了加载程序中着色器字符串的方法,读者可以自行体验其他两种着色器加载方式。

示例中着色器代码的开发,包括顶点着色器和片元着色器。着色器代码具体如下所示:

javascript 复制代码
 1     BABYLON.Effect.ShadersStore["customVertexShader"]= //顶点着色器
        2     "in vec3 position; \r\n"+                            //顶点坐标
        3     "in vec3 normal; \r\n"+                              //顶点法向量
        4     "uniform mat4 worldViewProjection; \r\n"+            //总变换矩阵
        5     "out vec4 vPosition; \r\n"+                          //输出片元着色器的顶点坐标
        6     "out vec3 vNormal; \r\n"+                            //输出片元着色器的法向量
        7     "void main() {\r\n"+
        8     "    vec4 p = vec4( position, 1. ); \r\n"+          //顶点坐标
        9     "    vPosition = p; \r\n"+                           //顶点坐标传递给片元着色器
        10    "    vNormal = normal; \r\n"+                        //法向量传递给片元着色器
        11    "    gl_Position = worldViewProjection * p; \r\n"+  //根据总变换矩阵计算此次绘制的顶
                    点位置
        12    "}\r\n";
        13    BABYLON.Effect.ShadersStore["customFragmentShader"]=  //片元着色器
        14    "precision highp float; \r\n"+                          //浮点数精度
        15    "uniform mat4 worldView; \r\n"+                         //视图矩阵
        16    "in vec4 vPosition; \r\n"+                              //从顶点着色器接收的顶点坐标
        17    "in vec3 vNormal; \r\n"+                                //从顶点着色器接收的法向量
        18    "uniform sampler2D refSampler; \r\n"+                   //纹理数据
        19    "void main() {\r\n"+
        20    "    vec3 e = normalize( vec3( worldView * vPosition ) ); \r\n"+    //将顶点坐标
              变换至相机坐标系下并单位化
        21    "    vec3 n = normalize( worldView * vec4(vNormal, 0.0) ).xyz; \r\n"+//将法向量变
              换至相机坐标系下并单位化
        22    "    vec3 r = reflect( e, n ); \r\n"+                   //求反射向量
        23    "    float m = 2. * sqrt(\r\n"+                   //求球形纹理的反射向量长度
        24    "        pow( r.x, 2. ) +\r\n"+
        25    "        pow( r.y, 2. ) +\r\n"+
        26    "        pow( r.z + 1., 2. )\r\n"+
        27    "    ); \r\n"+
        28    "    vec2 vN = r.xy / m + .5; \r\n"+         //单位化纹理的反射向量 xy坐标并右移0.5
        29    "    vec3 base = texture2D( refSampler, vN).rgb; \r\n"+  //纹理采样
        30    "    glFragColor = vec4( base, 1. ); \r\n"+               //输出到片元的颜色
        31    "}\r\n";

❑ 第1~12行为此程序的顶点着色器。其功能是根据总变换矩阵计算出每次绘制的顶点位置,并将顶点坐标和顶点法向量传递给片元着色器。

❑ 第13~31行为此程序的片元着色器。其功能是将顶点坐标和顶点法向量变化到摄像机坐标系,并求出顶点的反射向量,然后求出球形纹理上对应的反射向量,并计算出球形纹理的采样坐标,最后将纹理采样的颜色输出到片元。

相关推荐
黑听人28 分钟前
【力扣 中等 C++】90. 子集 II
开发语言·数据结构·c++·算法·leetcode
Wukong.Sun1 小时前
操作系统的概念,功能和目标
java·linux·开发语言·windows
虾球xz1 小时前
CppCon 2015 学习:Algorithmic Differentiation C++ & Extremum Estimation
开发语言·c++·学习
黑听人1 小时前
【力扣 简单 C】21. 合并两个有序链表
c语言·开发语言·数据结构·算法·leetcode
白露与泡影1 小时前
Java面试避坑指南:牛客网最新高频考点+答案详解
java·开发语言·面试
前端南玖1 小时前
Vue3响应式核心:ref vs reactive深度对比
前端·javascript·vue.js
@zcc@1 小时前
Java日期格式化
java·开发语言
微笑边缘的金元宝1 小时前
svg实现3环进度图,可动态调节进度数值,(vue)
前端·javascript·vue.js·svg
程序猿小D1 小时前
第28节 Node.js 文件系统
服务器·前端·javascript·vscode·node.js·编辑器·vim
黑听人1 小时前
【力扣 简单 C】83. 删除排序链表中的重复元素
c语言·开发语言·数据结构·算法·leetcode