当前位置: 棋牌电玩游戏平台 > 新闻中心 > 正文

Three.js基础探寻七——Lamber材质与Phong材质

时间:2019-07-02 12:24来源:新闻中心
【题外话】 材质(Material)是独立于物体顶点信息之外的与渲染效果相关的属性。通过设置材质可以改变物体的颜色、纹理贴图、光照模式等。 颜色模型 颜色的 特性 :色调,饱和度,

【题外话】

  材质(Material)是独立于物体顶点信息之外的与渲染效果相关的属性。通过设置材质可以改变物体的颜色、纹理贴图、光照模式等。

颜色模型

颜色的特性:色调,饱和度,量度。
光的波长范围:380nm --- 780nm

上一篇文章介绍了3D开发基础与XNA开发程序的整体结构,以及使用Model类的Draw方法将模型绘制到屏幕上。本文接着上一篇文章继续,介绍XNA中模型的结构、BasicEffect的使用以及用户输入和界面显示的方式等,本文尽量把遇到的概念都解析清楚,但又避开复杂的数学方面的知识,希望对没有接触过3D开发的同学有所帮助。

  本篇将介绍基本材质以及两种基于光照模型的材质(Lamber与Phong)。

RGB模型

红绿蓝是加性原色,各个原色混合在一起可以产生复合色

 

 

CMY模型

用红绿蓝的补色,青,品红,黄为原色构成。为碱性原色系统。常用于从白光中过滤某种颜色。用于印刷硬拷贝设备。


【系列索引】

  MeshBasicMaterial:对光照无感,给几何体一种简单的颜色或显示线框。

光照模型

在计算机图形学中为表述自然光照现象,需要根据光学物理的有关定律建立一个数学模型去计算景物表面上任意一点投向观察者眼中的光亮度的大小。

光照模型的影响因素: 物体的类型,物体相对于光源与其他物体的位置以及场景中所设置的光源属性,物体的透明体,物体的表面光亮程度,表面纹理,还有各种光源信息。

漫反射,环境光,镜面反射

简单光反射模型
入射光 = 漫反射光 镜面反射光 环境光

  1. 从零3D基础入门XNA 4.0(1)——3D开发基础
  2. 从零3D基础入门XNA 4.0(2)——模型和BasicEffect

  MeshLambertMaterial:这种材质对光照有反应,用于创建暗淡的不发光的物体。

环境光

是指光源间接对物体的影响,是在物体和环境之间多次反射,最终达到平衡时的一种光。
光强分布均匀,在任何一个方向上的分布都相同。

Ie=Ia·Ka
Ia :环境光强度 Ka:物体表面对环境光的反射系数

 

  MeshPhongMaterial:这种材质对光照也有反应,用于创建金属类明亮的物体。

漫反射光

光照射到粗糙无光泽表面的光现象。光线来源于一个方向,向各个方向反射。

Id =Ip·Kd ·cosθ         0≤θ≤π/2
Ip:入射光强度,
Kd:入射光的漫反射系数,范围: 0< Kd<1   

图片 1

漫反射光.PNG

【文章索引】

 

镜面反射光

有光泽的表面上能看到很强的高光,这个现象称为镜面反射。

Phong经验模型计算公式:Is =Ip·Ks ·cosa^n  0<a<π/2

Is:为镜面反射光在观察方向上的光强度;
Ip:为点光源的强度; Ks:为镜面反射系数;
a:为视点方向V与镜面反射方向R之间的夹角;
n:与物体表面光滑度有关的一个常数,
一般取为1~2000。表面↑,n↑。

表面越光滑,光强越小。因为表面光滑时光几乎全部集中在反射方向上了。

Phong光照模型:由物体表面上一点P反射到视点的光强I为环境光强Ie,理想漫反射光强Id和镜面反射光Is的总和。

图片 2

Phong.PNG


  1. Model模型的结构
  2. BasicEffect效果的设置
  3. XNA的用户输入
  4. XNA界面的显示方式

1.基本材质

光线追踪模型

整体光照模型
为了精确模拟光照效果,要考虑四种情况:镜面反射到镜面反射,镜面反射到漫反射,漫反射到镜面反射,漫反射到漫反射。投射可分为漫透射和规则投射。

Whitted光线追踪算法中采用了整体光照模型。
在简单光照模型的基础上加上了透射光一项。
![whitted.PNG](http://upload-images.jianshu.io/upload_images
/7110122-81d6ef054b9a2043.PNG?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

It为折射方向的入射光强度;Kt'为透射系数,为0~1之间的一个常数;其大小取决于物体的材料。

若透明体又是一个镜面反射体,应再加上反射光一项,以模拟镜面反射效果。

图片 3

whitted2.PNG

Is为镜面反射方向的入射光强度;Ks'为镜面反射系数,为0~1之间的一个常数,其大小同样取决于物体的材料。 

图片 4

计算反射方向和折射方向.PNG

 

  使用基本材质(BasicMaterial)的物体,渲染后物体的颜色始终为该材质的颜色,而不会由于光照产生明暗、阴影效果。如果没有指定材质的颜色,则颜色是随机的。其构造函数是:

光线跟踪算法

从视点出发,通过图像平面上每个像素中心向场景发出一条光线,光线的起点为视点,方向为像素中心和视点连线的单位向量。光线离视点最近的场景物体表面交点有三种可能。

  1. 当前交点所在的物体表面为理想漫射面,跟踪结束
  2. 当前交点所在的物体表面为理想镜面,光线沿其镜面反射方向继续跟踪。
  3. 当前交点所在的物体表面为规则投射面,光线沿其投射光想继续跟踪。
    终止条件:1)光线未碰到任何物体 2)光线碰到了背景
    3)光线经过许多次反射和折射后,光线对视点的光强小于某个设定值 4)光线反射或折射次数即跟踪深度大于一定的值
RayTracing (start, direction, weight, color) {  
    if ( weight < MinWeight )
        color = black;
    else { 
        计算光线与所有物体的交点中离start最近的点;
        if ( 没有交点 ) 
            color = black;
        else {   
            local = 在交点处用局部光照模型计算出的光强;
            计算反射方向 R;
            RayTracing(最近的交点, R, weight*Wr, Ir);
            计算折射方向 T;
            RayTracing(最近的交点, T, weight*Wt, It);
            color = Local   KsIr  KtIt;
         }
     }
}

【一、Model模型的结构】

THREE.MeshLambertMaterial(opt)

纹理映射技术

纹理:物体表面的各种细小结构和图案花纹

分类:二维&&三维,颜色纹理&&几何纹理&&过程纹理

图像纹理:将二维纹理图案映射到三维物体表面,绘制物体表面上一点时,采用相应的纹理图案中相应点的颜色值

函数纹理:用数学函数定义简单的二维纹理图案,如方格地毯,或用数学函数定义随机高度场,生成表面粗糙纹理即几何纹理。

纹理映射就是将在纹理空间中uv平面上预先定义的二维纹理(图像、图形、函数等)映射到景物空间的三维物体表面,再进一步映射到图像空间的二维图像平面上,一般将两个映射合并为一个映射。

颜色纹理映射要达到的目的是使绘制出来的物体表面具有花纹图案效果。它的基本思想是:
①给出期望在物体表面出现的花纹图案样式,可以用纹理函数来表示。纹理函数的定义域称为纹理定义域,纹理函数值一般可以理解为亮度值,可以转换为RGB表示的颜色值。
②建立物体表面的定义域与纹理函数的定义域之间的映射关系(即映射函数)。这种对应关系一旦建立,物体表面任何一点的花纹图案属性都可以通过纹理定义域中相应点的纹理由数值获得。
③在绘制物体表面可见点时,通过前面定义的对应关系可以获得该可见点处代表花纹图案属性的相应纹理函数值,适当地使用该纹理函数值就可以便最终绘制出来的物体表面具有花纹图案的效果。

图片 5

图片 6

反求

先对物体表面进行参数化,反求出物体表面的参数后,就可以根据(U,V)得到该出的纹理值,并用此代替光照模型中的相应项。

上一篇文章使用Model自带的Draw方法实现了直接将载入的Model绘制到指定的位置上去,但是有时候绘制出来的效果并不符合我们的预期,比如下图(下图的模型是通过Maya创建的一个屋子):

  其中,opt可以缺省,或者为包含各属性的值。如新建一个不透明度为0.75的黄色材质:

图片 7

new THREE.MeshBasicMaterial({

    color: 0xffff00,

    opacity: 0.75

});

通过ILSpy查看Microsoft.Xna.Framework.Graphics.Model,可以看到其Draw方法的代码如下:

  将其应用于一个正方体(方法参见3.js-4,效果为:

图片 8图片 9

图片 10

 1 public void Draw(Matrix world, Matrix view, Matrix projection)
 2 {
 3     int count = this.meshes.Count;
 4     int count2 = this.bones.Count;
 5     Matrix[] array = Model.sharedDrawBoneMatrices;
 6     if (array == null || array.Length < count2)
 7     {
 8         array = new Matrix[count2];
 9         Model.sharedDrawBoneMatrices = array;
10     }
11     this.CopyAbsoluteBoneTransformsTo(array);
12     for (int i = 0; i < count; i  )
13     {
14         ModelMesh modelMesh = this.meshes[i];
15         int index = modelMesh.ParentBone.Index;
16         int count3 = modelMesh.Effects.Count;
17         for (int j = 0; j < count3; j  )
18         {
19             Effect effect = modelMesh.Effects[j];
20             if (effect == null)
21             {
22                 throw new InvalidOperationException(FrameworkResources.ModelHasNoEffect);
23             }
24             IEffectMatrices effectMatrices = effect as IEffectMatrices;
25             if (effectMatrices == null)
26             {
27                 throw new InvalidOperationException(FrameworkResources.ModelHasNoIEffectMatrices);
28             }
29             effectMatrices.World = array[index] * world;
30             effectMatrices.View = view;
31             effectMatrices.Projection = projection;
32         }
33         modelMesh.Draw();
34     }
35 }

源码:

View Code

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>3.js测试7.1</title>
    </head>
    <body onload="init()">
        <canvas id="mainCanvas" width="400px" height="300px" ></canvas>
    </body>
    <script type="text/javascript" src="js/three.min.js"></script>
    <script type="text/javascript">
        function init() {
            var renderer = new THREE.WebGLRenderer({
                canvas: document.getElementById('mainCanvas')
            });
            renderer.setClearColor(0x000000);
            var scene = new THREE.Scene();

            // camera
            var camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
            camera.position.set(25, 25, 25);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
            scene.add(camera);

            // light
            var light = new THREE.PointLight(0xffffff, 1, 100);
            light.position.set(10, 15, 5);
            scene.add(light);

            var material = new THREE.MeshBasicMaterial({
                color: 0xffff00,
                opacity: 0.75
            });

            var cube = new THREE.Mesh(new THREE.CubeGeometry(5, 5, 5), material);
            scene.add(cube);

            renderer.render(scene, camera);
        }
    </script>
</html>

其中可见,Draw方法通过遍历模型的Mesh,然后再遍历每个Mesh的Effect,并对每个Effect进行设置,最后使用Mesh的Draw方法将其绘制到屏幕上。

 

为了了解Model的渲染,我们首先需要了解Model的结构。实际上,在一个Model对象中,包含Bone集合(model.Bones)、Mesh集合(model.Meshes)以及根Bone(model.Root)三个属性,其结构和关系如下:

  BasicMaterial的几个较为常用的属性:

图片 11

  · visible:是否可见,默认为true

可以看到对于每个ModelMesh,包含一组ModelMeshPart与一个ParentBone。其中,

  · side:渲染面片正面或是反面,默认为正面THREE.FrontSide,可设置为反面THREE.BackSide,或双面THREE.DoubleSide

  • ModelMesh表示单个可以独立移动的物理对象。例如,一个car的Model可以包含一个车体(body)的ModelMesh、四个车轮(wheel)的ModelMesh与一对门(door)的ModelMesh。
  • ModelMeshPart表示单个相同材料的部件,其代表一个单独的绘制调用(draw call)。例如,上述车身可以包含着色的表面、使用环境映射(environment mapping)效果的挡风玻璃以及使用法线贴图(normalmap texture)效果的座椅等等。
  • ModelBone表示了对应的ModelMesh如何变换,其包含一个Transform的变换矩阵。ModelBone是以树形存储的,每个ModelBone都有一个父节点以及若干个子节点。上述的每个ModelMesh都有一个ParentBone,ModelMesh可以根据ModelBone的变换来确定最终显示的位置等。例如,上述车门的ModelBone与车轮的ModelBone是车身的子节点等等。

  · wireframe:是否渲染线而非面,默认为false

所以遍历一个Model中所有的ModelMesh,然后遍历其中所有的ModelMeshPart,并且根据ModelMesh的ParentBone来将每一个ModelMeshPart绘制到指定的位置上就可以绘制出完整的Model。

  · color:十六进制RGB颜色,如红色表示为0xff0000

不过对于每个ModelMeshPart,其实际渲染的效果都存在Effect的属性中,对于默认来说,Effect均为BasicEffect。此外,对于ModelBone,其变换矩阵都是相对其自身的Parent来的,不过Model类也提供了一个方法,即CopyAbsoluteBoneTransformsTo(),即可将每个Bone相对于RootBone的变换矩阵复制到一个矩阵数组中,然后将其应用到Effect中即可。这种方式与上述提到的Model.Draw类似,不过自己写的话就可以自定义每个ModelMeshPart渲染的效果,当然也可以设置每个ModelMeshPart的渲染位置。

  · map:使用纹理贴图

那么接下来就按照这个思路去实现,同时在设置每一个Effect时,使用Effect提供的使用默认光照的方法EnableDefaultLighting(),启用后效果如下:

 

图片 12

  对于基本材质,即使改变场景中的光源,使用该材质的物体也始终为颜色处处相同的效果。当然,这不是很具有真实感,因此,接下来我们将介绍更为真实的光照模型:Lambert光照模型以及Phong光照模型。

这样的效果就达到了我们的预期,按上述的方法实现的代码如下:

 

图片 13图片 14

2.Lamber材质与Phong材质 

 1 Matrix world = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up);
 2 
 3 Matrix[] transforms = new Matrix[model.Bones.Count];
 4 this.model.CopyAbsoluteBoneTransformsTo(transforms);
 5 
 6 foreach (ModelMesh mesh in model.Meshes)
 7 {
 8     Int32 boneIndex = mesh.ParentBone.Index;
 9 
10     foreach (ModelMeshPart part in mesh.MeshParts)
11     {
12         BasicEffect effect = part.Effect as BasicEffect;
13         
14         effect.EnableDefaultLighting();
15         effect.World = transforms[boneIndex] * world;
16         effect.View = cameraView;
17         effect.Projection = cameraProjection;
18     }
19 
20     mesh.Draw();
21 }

  Lambert材质(MeshLambertMaterial)是符合Lambert光照模型的材质。Lambert光照模型的主要特点是只考虑漫反射而不考虑镜面反射的效果,因而对于金属、镜子等需要镜面反射效果的物体就不适应,对于其他大部分物体的漫反射效果都是适用的。

View Code

  其光照模型公式为:

不过这与刚才看到的Model.Draw的代码并不相同。实际上,XNA为了简化操作,已经将ModelMeshPart的每个Effect放到了ModelMesh的Effects集合中,只需要遍历这个集合就可以,而无需再遍历ModelMeshPart,再获取Effect了。所以上述代码可以简化为如下的代码:

Idiffuse = Kd * Id * cos(theta)

编辑:新闻中心 本文来源:Three.js基础探寻七——Lamber材质与Phong材质

关键词: .NET技术 C# .net XNA 3D