three.js 源码注释(四十五)Material /Material .js

时间:2022-04-22 03:30:21

商域无疆 (http://blog.csdn.net/omni360/)

本文遵循“署名-非商业用途-保持一致”创作公用协议

转载请保留此句:商域无疆 -  本博客专注于 敏捷开发及移动和物联设备研究:数据可视化、GOLANG、Html5、WEBGL、THREE.JS否则,出自本博客的文章拒绝转载或再转载,谢谢合作。


俺也是刚开始学,好多地儿肯定不对还请见谅.

以下代码是THREE.JS 源码文件中Material/Material .js文件的注释.

更多更新在 : https://github.com/omni360/three.js.sourcecode


/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */
/*
///Material是材质对象的抽象基类,当创建材质时都从这个类继承.Material对象的功能函数采用定义构造的函数原型对象来实现.
/// 简单的说就是物体看起来是什么质地。材质可以看成是材料和质感的结合。在渲染程式中,它是表面各可视属性的结合,
/// 这些可视属性是指表面的色彩、纹理、光滑度、透明度、反射率、折射率、发光度等。
///
*/
///<summary>Material</summary>
THREE.Material = function () {

	this.id = THREE.MaterialIdCount ++;		//材质属性id
	this.uuid = THREE.Math.generateUUID();	//材质uuid(通用唯一标识码)属性

	this.name = '';		//材质名称属性,可有可无

	this.side = THREE.FrontSide;	//模型正面只附着材质,还有选项THREE.BackSide 材质只附着背面 ,和选项THREE.DoubleSide = 2;	模型双面都附着材质

	this.opacity = 1;		//属性opacity为一个0-1区间的值,表明透明度。属性transparent指定是否使用透明,只有在该值为真的时候,才会将其与混合(透明是渲染像素时,待渲染值与已存在值共同作用计算出渲染后像素值,达到混合的效果)。
	this.transparent = false;	//是否使用透明.

	//属性blending,blendSrc,blendDst,blendEquation指定了混合方式和混合源Src和混合像素已有的像元值Dst的权重指定方式。默认情况下(如构造函数中赋的缺省值),新的像元值等于:新值×alpha+旧值×(1-alpha)。
	this.blending = THREE.NormalBlending;	//材质混合混合模式类型
	//材质混合混合模式类型,有相加,相减,相乘,自定义等将不同的材质,颜色混合的方式
	//TODO:有时间可以自定义几种混合模式试试.实现一些特殊的效果.

	//参考:http://blog.csdn.net/tudoumayi/article/details/6489575
	//参考:https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.13
	//参考:https://www.opengl.org/registry/specs/EXT/blend_color.txt
	//参考:http://blog.sina.com.cn/s/blog_9f4bc8e301014m6c.html
	//参考:https://www.opengl.org/registry/specs/EXT/blend_color.txt
	//参考:https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBlendFuncSeparate.xml
	/*************************************摘抄自王路杰滴博客,http://blog.sina.com.cn/s/blog_9f4bc8e301014m6c.html***********

	混合是什么呢?混合就是把两种颜色混在一起。具体一点,就是把某一像素位置原来的颜色和将要画上去的颜色,通过某种方式混在一起,从而实现特殊的效果。
	假设我们需要绘制这样一个场景:透过红色的玻璃去看绿色的物体,那么可以先绘制绿色的物体,再绘制红色玻璃。在绘制红色玻璃的时候,利用“混合”功能,
	把将要绘制上去的红色和原来的绿色进行混合,于是得到一种新的颜色,看上去就好像玻璃是半透明的。
	要使用OpenGL的混合功能,只需要调用:glEnable(GL_BLEND);即可。
	要关闭OpenGL的混合功能,只需要调用:glDisable(GL_BLEND);即可。
	注意:只有在RGBA模式下,才可以使用混合功能,颜色索引模式下是无法使用混合功能的。
	一、源因子和目标因子
	前面我们已经提到,混合需要把原来的颜色和将要画上去的颜色找出来,经过某种方式处理后得到一种新的颜色。
	这里把将要画上去的颜色称为“源颜色”,把原来的颜色称为“目标颜色”。
	OpenGL 会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘以的系数称为“源因子”,目标颜色乘以的系数称为“目标因子”),然后相加,这样就得到了新的颜色。
	(也可以不是相加,新版本的OpenGL可以设置运算方式,包括加、减、取两者中较大的、取两者中较小的、逻辑运算等,但我们这里为了简单起见,不讨论这个了)
	下面用数学公式来表达一下这个运算方式。假设源颜色的四个分量(指红色,绿色,蓝色,alpha值)是(Rs, Gs, Bs,  As),目标颜色的四个分量是(Rd, Gd, Bd, Ad),
	又设源因子为(Sr, Sg, Sb, Sa),目标因子为(Dr, Dg, Db,  Da)。则混合产生的新颜色可以表示为:
	(Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da)
	当然了,如果颜色的某一分量超过了1.0,则它会被自动截取为1.0,不需要考虑越界的问题。

	源因子和目标因子是可以通过glBlendFunc函数来进行设置的。glBlendFunc有两个参数,前者表示源因子,后者表示目标因子。这两个参数可以是多种值,下面介绍比较常用的几种。
	GL_ZERO:     表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。
	GL_ONE:      表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
	GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
	GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
	GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
	GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。
	除 此以外,还有GL_SRC_COLOR(把源颜色的四个分量分别作为因子的四个分量)、GL_ONE_MINUS_SRC_COLOR、 GL_DST_COLOR、GL_ONE_MINUS_DST_COLOR等,前两个在OpenGL旧版本中只能用于设置目标因子,
	后两个在OpenGL 旧版本中只能用于设置源因子。新版本的OpenGL则没有这个限制,并且支持新的GL_CONST_COLOR(设定一种常数颜色,将其四个分量分别作为因子的四个分量)、GL_ONE_MINUS_CONST_COLOR、GL_CONST_ALPHA、
	 GL_ONE_MINUS_CONST_ALPHA。另外还有GL_SRC_ALPHA_SATURATE。新版本的OpenGL还允许颜色的alpha 值和RGB值采用不同的混合因子。但这些都不是我们现在所需要了解的。毕竟这还是入门教材,不需要整得太复杂~

	举例来说:
	如果设置了glBlendFunc(GL_ONE, GL_ZERO);,则表示完全使用源颜色,完全不使用目标颜色,因此画面效果和不使用混合的时候一致(当然效率可能会低一点点)。如果没有设置源因子和目标因子,则默认情况就是这样的设置。
	如果设置了glBlendFunc(GL_ZERO, GL_ONE);,则表示完全不使用源颜色,因此无论你想画什么,最后都不会被画上去了。(但这并不是说这样设置就没有用,有些时候可能有特殊用途)
	如果设置了glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,则表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,
	                                                                而目标颜色所占比例则减小。这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
	如果设置了glBlendFunc(GL_ONE, GL_ONE);,则表示完全使用源颜色和目标颜色,最终的颜色实际上就是两种颜色的简单相加。例如红色(1, 0, 0)和绿色(0, 1, 0)相加得到(1, 1, 0),结果为黄色。
	注意:
	        所谓源颜色和目标颜色,是跟绘制的顺序有关的。假如先绘制了一个红色的物体,再在其上绘制绿色的物体。则绿色是源颜色,红色是目标颜色。如果顺序反过来,则红色就是源颜色,绿色才是目标颜色。
	        在绘制时,应该注意顺序,使得绘制的源颜色与设置的源因子对应,目标颜色与设置的目标因子对应。不要被混乱的顺序搞晕了。
	 
	一、源因子和目标因子
	前面我们已经提到,混合需要把原来的颜色和将要画上去的颜色找出来,经过某种方式处理后得到一种新的颜色。这里把将要画上去的颜色称为“源颜色”,把原来的颜色称为“目标颜色”。
	OpenGL 会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘以的系数称为“源因子”,目标颜色乘以的系数称为“目标因子”),然后相加,这样就得到了新的颜色。(也可以不是相加,
	新版本的OpenGL可以设置运算方式,包括加、减、取两者中较大的、取两者中较小的、逻辑运算等,但我们这里为了简单起见,不讨论这个了)
	下面用数学公式来表达一下这个运算方式。假设源颜色的四个分量(指红色,绿色,蓝色,alpha值)是(Rs, Gs, Bs,  As),目标颜色的四个分量是(Rd, Gd, Bd, Ad),又设源因子为(Sr, Sg, Sb, Sa),
	目标因子为(Dr, Dg, Db,  Da)。则混合产生的新颜色可以表示为:
	        (Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da)
	当然了,如果颜色的某一分量超过了1.0,则它会被自动截取为1.0,不需要考虑越界的问题。

	源因子和目标因子是可以通过glBlendFunc函数来进行设置的。glBlendFunc有两个参数,前者表示源因子,后者表示目标因子。这两个参数可以是多种值,下面介绍比较常用的几种。
	GL_ZERO:     表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。
	GL_ONE:      表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
	GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
	GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
	GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
	GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。
	除 此以外,还有GL_SRC_COLOR(把源颜色的四个分量分别作为因子的四个分量)、GL_ONE_MINUS_SRC_COLOR、 GL_DST_COLOR、GL_ONE_MINUS_DST_COLOR等,前两个在OpenGL旧版本中只能用于设置目标因子,
	后两个在OpenGL 旧版本中只能用于设置源因子。新版本的OpenGL则没有这个限制,并且支持新的GL_CONST_COLOR(设定一种常数颜色,将其四个分量分别作为因子的四个分量)、GL_ONE_MINUS_CONST_COLOR、
	GL_CONST_ALPHA、 GL_ONE_MINUS_CONST_ALPHA。另外还有GL_SRC_ALPHA_SATURATE。新版本的OpenGL还允许颜色的alpha 值和RGB值采用不同的混合因子。但这些都不是我们现在所需要了解的。毕竟这还是入门教材,不需要整得太复杂~

	举例来说:
	如果设置了glBlendFunc(GL_ONE, GL_ZERO);,则表示完全使用源颜色,完全不使用目标颜色,因此画面效果和不使用混合的时候一致(当然效率可能会低一点点)。如果没有设置源因子和目标因子,则默认情况就是这样的设置。
	如果设置了glBlendFunc(GL_ZERO, GL_ONE);,则表示完全不使用源颜色,因此无论你想画什么,最后都不会被画上去了。(但这并不是说这样设置就没有用,有些时候可能有特殊用途)
	如果设置了glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,则表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减小。
	这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
	如果设置了glBlendFunc(GL_ONE, GL_ONE);,则表示完全使用源颜色和目标颜色,最终的颜色实际上就是两种颜色的简单相加。例如红色(1, 0, 0)和绿色(0, 1, 0)相加得到(1, 1, 0),结果为黄色。
	注意:
	所谓源颜色和目标颜色,是跟绘制的顺序有关的。假如先绘制了一个红色的物体,再在其上绘制绿色的物体。则绿色是源颜色,红色是目标颜色。如果顺序反过来,则红色就是源颜色,绿色才是目标颜色。
	在绘制时,应该注意顺序,使得绘制的源颜色与设置的源因子对应,目标颜色与设置的目标因子对应。不要被混乱的顺序搞晕了。

	三、实现三维混合
	也许你迫不及待的想要绘制一个三维的带有半透明物体的场景了。但是现在恐怕还不行,还有一点是在进行三维场景的混合时必须注意的,那就是深度缓冲。
	深度缓冲是这样一段数据,它记录了每一个像素距离观察者有多近。在启用深度缓冲测试的情况下,如果将要绘制的像素比原来的像素更近,则像素将被绘制。否则,像素就会被忽略掉,不进行绘制。
	这在绘制不透明的物体时非常有用——不管是先绘制近的物体再绘制远的物体,还是先绘制远的物体再绘制近的物体,或者干脆以混乱的顺序进行绘制,最后的显示结果总是近的物体遮住远的物体。
	然而在你需要实现半透明效果时,发现一切都不是那么美好了。如果你绘制了一个近距离的半透明物体,则它在深度缓冲区内保留了一些信息,使得远处的物体将无法再被绘制出来。虽然半透明的物体仍然半透明,但透过它看到的却不是正确的内容了。
	要解决以上问题,需要在绘制半透明物体时将深度缓冲区设置为只读,这样一来,虽然半透明物体被绘制上去了,深度缓冲区还保持在原来的状态。如果再有一个物体出现在半透明物体之后,在不透明物体之前,
	则它也可以被绘制(因为此时深度缓冲区中记录的是那个不透明物体的深度)。以后再要绘制不透明物体时,只需要再将深度缓冲区设置为可读可写的形式即可。嗯?你问我怎么绘制一个一部分半透明一部分不透明的物体?这个好办,
	只需要把物体分为两个部分,一部分全是半透明的,一部分全是不透明的,分别绘制就可以了。
	即使使用了以上技巧,我们仍然不能随心所欲的按照混乱顺序来进行绘制。必须是先绘制不透明的物体,然后绘制透明的物体。否则,假设背景为蓝色,近处一块红色玻璃,中间一个绿色物体。
	如果先绘制红色半透明玻璃的话,它先和蓝色背景进行混合,则以后绘制中间的绿色物体时,想单独与红色玻璃混合已经不能实现了。
	总结起来,绘制顺序就是:首先绘制所有不透明的物体。如果两个物体都是不透明的,则谁先谁后都没有关系。然后,将深度缓冲区设置为只读。接下来,绘制所有半透明的物体。
	如果两个物体都是半透明的,则谁先谁后只需要根据自己的意愿(注意了,先绘制的将成为“目标颜色”,后绘制的将成为“源颜色”,所以绘制的顺序将会对结果造成一些影响)。最后,将深度缓冲区设置为可读可写形式。
	调用glDepthMask(GL_FALSE);可将深度缓冲区设置为只读形式。调用glDepthMask(GL_TRUE);可将深度缓冲区设置为可读可写形式。
	一些网上的教程,包括大名鼎鼎的NeHe教程,都在使用三维混合时直接将深度缓冲区禁用,即调用glDisable(GL_DEPTH_TEST);。这样做并不正确。如果先绘制一个不透明的物体,再在其背后绘制半透明物体,
	本来后面的半透明物体将不会被显示(被不透明的物体遮住了),但如果禁用深度缓冲,则它仍然将会显示,并进行混合。NeHe提到某些显卡在使用glDepthMask函数时可能存在一些问题,但可能是由于我的阅历有限,并没有发现这样的情况。

	那么,实际的演示一下吧。我们来绘制一些半透明和不透明的球体。假设有三个球体,一个红色不透明的,一个绿色半透明的,一个蓝色半透明的。红色最远,绿色在中间,蓝色最近。根据前面所讲述的内容,红色不透明球体必须首先绘制,
	而绿色和蓝色则可以随意修改顺序。这里为了演示不注意设置深度缓冲的危害,我们故意先绘制最近的蓝色球体,再绘制绿色球体。
	为了让这些球体有一点立体感,我们使用光照。在(1, 1, -1)处设置一个白色的光源。代码如下:
	void setLight(void)
	{
	    static const GLfloat light_position[] = {1.0f, 1.0f, -1.0f, 1.0f};
	    static const GLfloat light_ambient[]  = {0.2f, 0.2f, 0.2f, 1.0f};
	    static const GLfloat light_diffuse[]  = {1.0f, 1.0f, 1.0f, 1.0f};
	    static const GLfloat light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};

	    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	    glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
	    glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
	    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);

	    glEnable(GL_LIGHT0);
	    glEnable(GL_LIGHTING);
	    glEnable(GL_DEPTH_TEST);
	}
	每一个球体颜色不同。所以它们的材质也都不同。这里用一个函数来设置材质。
	void setMatirial(const GLfloat mat_diffuse[4], GLfloat mat_shininess)
	{
	    static const GLfloat mat_specular[] = {0.0f, 0.0f, 0.0f, 1.0f};
	    static const GLfloat mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};

	    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_diffuse);
	    glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
	    glMaterialfv(GL_FRONT, GL_EMISSION,  mat_emission);
	    glMaterialf (GL_FRONT, GL_SHININESS, mat_shininess);
	}
	有了这两个函数,我们就可以根据前面的知识写出整个程序代码了。这里只给出了绘制的部分,其它部分大家可以自行完成。
	void myDisplay(void)
	{
	    // 定义一些材质颜色
	    const static GLfloat red_color[] = {1.0f, 0.0f, 0.0f, 1.0f};
	    const static GLfloat green_color[] = {0.0f, 1.0f, 0.0f, 0.3333f};
	    const static GLfloat blue_color[] = {0.0f, 0.0f, 1.0f, 0.5f};

	    // 清除屏幕
	    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	    // 启动混合并设置混合因子
	    glEnable(GL_BLEND);
	    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	    // 设置光源
	    setLight();

	    // 以(0, 0, 0.5)为中心,绘制一个半径为.3的不透明红色球体(离观察者最远)
	    setMatirial(red_color, 30.0);
	    glPushMatrix();
	    glTranslatef(0.0f, 0.0f, 0.5f);
	    glutSolidSphere(0.3, 30, 30);
	    glPopMatrix();

	    // 下面将绘制半透明物体了,因此将深度缓冲设置为只读
	    glDepthMask(GL_FALSE);

	    // 以(0.2, 0, -0.5)为中心,绘制一个半径为.2的半透明蓝色球体(离观察者最近)
	    setMatirial(blue_color, 30.0);
	    glPushMatrix();
	    glTranslatef(0.2f, 0.0f, -0.5f);
	    glutSolidSphere(0.2, 30, 30);
	    glPopMatrix();

	    // 以(0.1, 0, 0)为中心,绘制一个半径为.15的半透明绿色球体(在前两个球体之间)
	    setMatirial(green_color, 30.0);
	    glPushMatrix();
	    glTranslatef(0.1, 0, 0);
	    glutSolidSphere(0.15, 30, 30);
	    glPopMatrix();

	    // 完成半透明物体的绘制,将深度缓冲区恢复为可读可写的形式
	    glDepthMask(GL_TRUE);

	    glutSwapBuffers();
	}

	大家也可以将上面两处glDepthMask删去,结果会看到最近的蓝色球虽然是半透明的,但它的背后直接就是红色球了,中间的绿色球没有被正确绘制。

	小结:
	本课介绍了OpenGL混合功能的相关知识。
	混合就是在绘制时,不是直接把新的颜色覆盖在原来旧的颜色上,而是将新的颜色与旧的颜色经过一定的运算,从而产生新的颜色。新的颜色称为源颜色,原来旧的颜色称为目标颜色。传统意义上的混合,
	是将源颜色乘以源因子,目标颜色乘以目标因子,然后相加。
	源因子和目标因子是可以设置的。源因子和目标因子设置的不同直接导致混合结果的不同。将源颜色的alpha值作为源因子,用1.0减去源颜色alpha值作为目标因子,是一种常用的方式。
	这时候,源颜色的alpha值相当于“不透明度”的作用。利用这一特点可以绘制出一些半透明的物体。
	在进行混合时,绘制的顺序十分重要。因为在绘制时,正要绘制上去的是源颜色,原来存在的是目标颜色,因此先绘制的物体就成为目标颜色,后来绘制的则成为源颜色。绘制的顺序要考虑清楚,
	将目标颜色和设置的目标因子相对应,源颜色和设置的源因子相对应。
	在进行三维混合时,不仅要考虑源因子和目标因子,还应该考虑深度缓冲区。必须先绘制所有不透明的物体,再绘制半透明的物体。在绘制半透明物体时前,还需要将深度缓冲区设置为只读形式,
	否则可能出现画面错误。
	*************************************************************************************************************************/
	/***************自定义混合源和目标颜色因子**************************************************************************************
	THREE.ZeroFactor = 200;                       //GL_ZERO:     表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算
	THREE.OneFactor = 201;                        //GL_ONE:      表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
	THREE.SrcColorFactor = 202;                   //GL_SRC_COLOR  表示使用源颜色的四个分量分别作为因子的四个分量
	THREE.OneMinusSrcColorFactor = 203;           //GL_ONE_MINUS_SRC_COLOR 表示使用1.0减去源颜色的四个分量分别作为因子的四个分量
	THREE.SrcAlphaFactor = 204;                   //GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
	THREE.OneMinusSrcAlphaFactor = 205;           //GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
	THREE.DstAlphaFactor = 206;                   //GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
	THREE.OneMinusDstAlphaFactor = 207;           //GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。

	//custom blending source factors
	// 自定义混合模式源颜色因子

	//THREE.ZeroFactor = 200;
	//THREE.OneFactor = 201;
	//THREE.SrcAlphaFactor = 204;
	//THREE.OneMinusSrcAlphaFactor = 205;
	//THREE.DstAlphaFactor = 206;
	//THREE.OneMinusDstAlphaFactor = 207;
	THREE.DstColorFactor = 208;                   //GL_DST_COLOR  表示使用目标颜色的四个分量分别作为因子的四个分量
	THREE.OneMinusDstColorFactor = 209;           //GL_ONE_MINUS_DST_COLOR  表示使用1.0减去目标颜色的四个分量分别作为因子的四个分量
	THREE.SrcAlphaSaturateFactor = 210;           //GL_SRC_ALPHA_SATURATE  表示源颜色的alpha 值和RGB值采用不同的混合因子
	*****************************************************************************************************************************/
	this.blendSrc = THREE.SrcAlphaFactor;	//混合颜色的源颜色因子,默认为THREE.SrcAlphaFactor. GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。其它选项参考上面注释
	this.blendDst = THREE.OneMinusSrcAlphaFactor;	//混合颜色的目标颜色因子,默认为THREE.OneMinusSrcAlphaFactor. GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。其它选项参考上面注释

	// 参考:https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBlendEquation.xml
	// 参考:https://www.opengl.org/wiki/Blending
	// 参考:http://blog.sina.com.cn/s/blog_7b62c61c01016nnc.html
	/*********************************************设置混合方程式*********************************************************
	设置混合方程式:glBlendEquation(GLenum mode);
	        GLenum mode可取的值:
	        GL_FUNC_ADD                        Cf = (Cs * S)+(Cd * D)
	        GL_FUNC_SUBTRACT                   Cf = (Cs * S)-(Cd * D)   
	        GL_FUNC_RESERSE_SUBTRACT           Cf = (Cd * D)-(Cs * S)
	        GL_MIN                             Cf = min(Cs,Cd)
	        GL_MAX                             Cf = max(Cs,Cd)
	其中:
	        a. Cf表示混合后显示的颜色
	        b. Cd混合前颜色缓冲中已经有的颜色值
	        c. Cs将要绘制的颜色
	        d. S为glBlendFunc函数设置时的第一个参数,源颜色因子
	        e. D为glBlendFunc函数设置时的第二个参数,目标颜色因子
	**********************************************设置混合方程式*********************************************************/
	this.blendEquation = THREE.AddEquation;		//混合方程式,默认为THREE.AddEquation,相加,看上面公式
	/************************深度测试************************************************************************************
	参考:http://blog.csdn.net/zhongjling/article/details/7573055
	openGL里常出现深度测试,一直不清楚。今天就来弄清楚。
	  (1)什么是深度?
	        深度其实就是该象素点在3d世界中距离摄象机的距离(绘制坐标),深度缓存中存储着每个象素点(绘制在屏幕上的)的深度值!
	   深度值(Z值)越大,则离摄像机越远。
	   深度值是存贮在深度缓存里面的,我们用深度缓存的位数来衡量深度缓存的精度。深度缓存位数越高,则精确度越高,目前的显卡一般都可支持16位的Z Buffer,一些高级的显卡已经可以支持32位的Z Buffer,但一般用24位Z Buffer就已经足够了。
	  (2)为什么需要深度?
	   在不使用深度测试的时候,如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这样的效果并不是我们所希望的。而有了深度缓冲以后,绘制物体的顺序就不那么重要了,都能按照远近(Z值)正常显示,这很关键。
	        实际上,只要存在深度缓冲区,无论是否启用深度测试,OpenGL在像素被绘制时都会尝试将深度数据写入到缓冲区内,除非调用了glDepthMask(GL_FALSE)来禁止写入。这些深度数据除了用于常规的测试外,还可以有一些有趣的用途,比如绘制阴影等等。
	 (2)启用深度测试
	     使用 glEnable(GL_DEPTH_TEST);
	     在默认情况是将需要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,如果比深度缓存中的值小,那么用新像素的颜色值更新帧缓存中对应像素的颜色值。
	     但是可以使用glDepthFunc(func)来对这种默认测试方式进行修改。
	     其中参数func的值可以为GL_NEVER(没有处理)、GL_ALWAYS(处理所有)、GL_LESS(小于)、GL_LEQUAL(小于等于)、GL_EQUAL(等于)、GL_GEQUAL(大于等于)、GL_GREATER(大于)或GL_NOTEQUAL(不等于),其中默认值是GL_LESS。
	    一般来将,使用glDepthFunc(GL_LEQUAL);来表达一般物体之间的遮挡关系。 
	(3)启用了深度测试,那么这就不适用于同时绘制不透明物体。

	备注:
		绘制半透明物体时,需注意:在绘制半透明物体时前,还需要利用glDepthMask(GL_FALSE)将深度缓冲区设置为只读形式,否则可能出现画面错误。为什么呢,因为画透明物体时,将使用混色,这时就不能继续使用深度模式,而是利用混色函数来进行混合。这一来,就可以使用混合函数绘制半透明物体了。

	*****************************深度测试*******************************************************************************/
	this.depthTest = true;		//深度测试,默认为true,如果设置为false,在场景中远处的对象不被近处的对象遮挡

	/*****************************************************************************************************************
	如果flag是GL_FLASE,那么向深度缓冲区写入是禁止的。否则,就是允许的。初始时,是允许向深度缓冲区写入数据的。
	glDepthMask指定是否允许向深度缓冲区写入数据。如果flag是GL_FLASE,那么向深度缓冲区写入是禁止的。否则,就是允许的。
	初始时,是允许向深度缓冲区写入数据的。
	如果在glBegin和glEnd之间执行glDepthMask,会产生GL_INVALID_OPERATION。
	参考:http://blog.csdn.net/wlsgzl/article/details/13022959
	******************************************************************************************************************/
	this.depthWrite = true;		//允许或禁止向深度缓冲区写入数据,默认为true,指定是否允许向深度缓冲区写入数据。


	this.polygonOffset = false;	//多边形位移,当两个面共面时,会出现十分难看的z - fighting 问题,要解决此问题可以使用, Polygon Offset,
								//参考:https://www.opengl.org/sdk/docs/man2/xhtml/glPolygonOffset.xml
	this.polygonOffsetFactor = 0;	//多边形位移因子
	this.polygonOffsetUnits = 0;	//多边形位移单位

	/*************************alpha测试*******************************************************************************
	参考:http://blog.sina.com.cn/s/blog_471132920101d8z1.html
	原创文章如需转载请注明:转载自风宇冲Unity3D教程学院
	                       Shader第十二讲 Alpha测试

	引言:本讲和下一讲主要讲Alpha即透明通道有关的内容。RGBA,其中最终在屏幕上显示的只有RGB即 红绿蓝组成的颜色,Alpha并没有任何颜色显示。A只是辅助颜色运算特别是多图层运算而已。例如在单图层的情况下(1,1,1,0.8)输出的是(1,1,1)*0.8即 浅灰色(0.8,0.8,0.8),和(0.8,0.8,0.8)是等价的.但是假如不再是单图层而背景为绿色(0,1,0,1)的时候,(0.8,0.8,0.8)就不知道怎么去运算了,而(1,1,1,0.8)知道。看不懂这段引言?没关系,往下看,当学完第五讲和第六讲后再回来看就自然能深刻理解了。

	什么是通道:
	通道是8位的灰度图像,能够显示256种灰度,用来表示颜色的RGBA值。R=Red红色,G=Green绿色,B=blue蓝色,A=alhpa透明值, 其中RGB为原色通道,A为透明通道。
	在通道中,以白色代替透明表示要处理的部分(选择区域);以黑色表示不需处理的部分(非选择区域)
	所有通道取值范围是 [0,255], 但是在Shader里取值范围是[0,1], 0和1分辨对应0和255
	什么是Alpha通道:
	定义:一般图形卡具有32位总线,附加的8位信号就被用来保存不可见的透明度信号以方便处理用,这就是Alpha通道。白色的alpha象素用以定义不透明的彩色象素,而黑色的alpha象素用以定义透明象素,黑白之间的灰阶用来定义半透明象素。
	简而言之:Alpha通道取值同样为[0,255] 以及Shader[0,1], 值越小最透明,值最大越不透明。在Shader里即0为透明,什么都没有,编辑器显示为黑色。1则不透明,全部都显示,编辑器其显示为白色如下图。
	【风宇冲】Unity3D教程宝典之Shader篇:第十二讲 <wbr>Alpha测试

	什么是Alpha Test:
	Alpha Test ,中文就是透明度测试。简而言之就是V&F shader中最后fragment函数输出的该点颜色值(即上一讲frag的输出half4)的alpha值与固定值进行比较。 AlphaTest语句通常位于Pass{}中的起始位置。

	语法:
	第一种: AlphaTest Off: 不测试,全渲染
	第二种:Alpha 比较符 目标alpha值
	其中目标alpha值取值范围是 0至1, 也可以用参数 ,如 AlphaTest [varName]。

	比较符:(目标alpha值下面记作x)
	Always  全渲染(任意x值)
	Never   全不渲染
	Greater  点的alpha值大于x时渲染
	GEuqal   点的alpha值大于等于x时渲染
	Less       点的alpha值小于x时渲染
	LEqual    点的alpha值小于等于x时渲染
	Equal  点的alpha值等于x时渲染
	NotEqual  点的alpha值不等于x时渲染

	例:
	AlphaTest never  0      全不渲染(这个0可以是任意值,但必须填)
	AlphaTest always 0      全渲染(这个0可以是任意值,但必须填)
	AlphaTest off               全渲染
	AlphaTest Greater 0.4  当alpha大于0.4渲染,   缺点是边缘过于锋利并且显示不完整,alpha小于0.4的部分被忽略掉了
	Alpha Blend                 边缘柔和,但是中心也太柔和,没有实体的感觉
	AlphaTest Greater 0.4 / AlphaTest LEqual 0.4 两个部分,大于0.4不混合,小于0.4混合。效果是既有实体感觉,边缘也柔和。
	【风宇冲】Unity3D教程宝典之Shader篇:第十二讲 <wbr>Alpha测试


	*************************alpha测试*********************************************************************************/
	this.alphaTest = 0;	//alpha测试,取值范围0.0-1.0

	this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer
						// 当三角面之间产生间距,发生图形走样时,填充像素,确保图形保真,消除走样.通常取值范围在0.0=1.0之间.

	this.visible = true;	//可见性,默认为true

	this.needsUpdate = true;	//当设置为true时,标记材质已经更新.

};

/****************************************
****下面是Fog对象提供的功能函数.
****************************************/

THREE.Material.prototype = {

	constructor: THREE.Material,		//构造器,返回对创建此对象的Material函数的引用

	/*
	///setValues方法用来通过参数values设置材质对象的属性.values参数的格式为
	 values = {
	 *  color: <hex>,
	 *  opacity: <float>,
	 *  map: new THREE.Texture( <Image> ),
	 *
	 *  lightMap: new THREE.Texture( <Image> ),
	 *
	 *  specularMap: new THREE.Texture( <Image> ),
	 *
	 *  alphaMap: new THREE.Texture( <Image> ),
	 *
	 *  envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
	 *  combine: THREE.Multiply,
	 *  reflectivity: <float>,
	 *  refractionRatio: <float>,
	 *
	 *  shading: THREE.SmoothShading,
	 *  blending: THREE.NormalBlending,
	 *  depthTest: <bool>,
	 *  depthWrite: <bool>,
	 *
	 *  wireframe: <boolean>,
	 *  wireframeLinewidth: <float>,
	 *
	 *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
	 *
	 *  skinning: <bool>,
	 *  morphTargets: <bool>,
	 *
	 *  fog: <bool>
	 * }
	*/
	///<summary>setValues</summary>
	///<param name ="values" type="Object">材质属性值</param>
	///<returns type="Material">返回新的材质对象</returns>
	setValues: function ( values ) {

		if ( values === undefined ) return;

		for ( var key in values ) {	//遍历values中的键值,并一一赋值给当前材质对象.

			var newValue = values[ key ];

			if ( newValue === undefined ) {

				console.warn( "THREE.Material: '" + key + "' parameter is undefined." );
				continue;

			}

			if ( key in this ) {

				var currentValue = this[ key ];

				if ( currentValue instanceof THREE.Color ) {

					currentValue.set( newValue );

				} else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) {

					currentValue.copy( newValue );

				} else if ( key == 'overdraw' ) {

					// ensure overdraw is backwards-compatable with legacy boolean type
					this[ key ] = Number( newValue );

				} else {

					this[ key ] = newValue;

				}

			}

		}

	},

	/*clone方法
	///clone方法克隆一个材质对象.
	*/
	///<summary>clone</summary>
	///<param name ="material" type="Material">接收结果的材质对象</param>
	///<returns type="Texture">返回克隆的材质对象</returns>	
	clone: function ( material ) {
		//一下将材质属性一一复制.
		if ( material === undefined ) material = new THREE.Material();

		material.name = this.name;

		material.side = this.side;

		material.opacity = this.opacity;
		material.transparent = this.transparent;

		material.blending = this.blending;

		material.blendSrc = this.blendSrc;
		material.blendDst = this.blendDst;
		material.blendEquation = this.blendEquation;

		material.depthTest = this.depthTest;
		material.depthWrite = this.depthWrite;

		material.polygonOffset = this.polygonOffset;
		material.polygonOffsetFactor = this.polygonOffsetFactor;
		material.polygonOffsetUnits = this.polygonOffsetUnits;

		material.alphaTest = this.alphaTest;

		material.overdraw = this.overdraw;

		material.visible = this.visible;

		return material;	//返回克隆的材质对象

	},
	/*dispose方法
	///dispose方法从内存中删除对象,释放资源.
	///NOTE: 当删除材质对象,不要忘记调用这个方法,否则会导致内存泄露.
	*/
	///<summary>dispose</summary>
	dispose: function () {

		this.dispatchEvent( { type: 'dispose' } );

	}

};
///EventDispatcher方法应用到当前Material对象.
THREE.EventDispatcher.prototype.apply( THREE.Material.prototype );
///设置全局的Material对象计数器.
THREE.MaterialIdCount = 0;


商域无疆 (http://blog.csdn.net/omni360/)

本文遵循“署名-非商业用途-保持一致”创作公用协议

转载请保留此句:商域无疆 -  本博客专注于 敏捷开发及移动和物联设备研究:数据可视化、GOLANG、Html5、WEBGL、THREE.JS否则,出自本博客的文章拒绝转载或再转载,谢谢合作。


以下代码是THREE.JS 源码文件中Material/Material .js文件的注释.

更多更新在 : https://github.com/omni360/three.js.sourcecode