《webgl入门指南》学习笔记三之three.js创建多重纹理

时间:2022-07-06 04:25:27

之前的demo看起来已经很真了,当然是在大家,一步一步step by step的情况下,被各种初始化的代码虐的体无完肤后的,才会有这么个感受。但是从一个对计算机图形无感的人来看,这当然是很粗糙的,比如看起来比较‘平’光线好像,对比不正常,所以我们现在要使用多重纹理,在这个新的demo里面我们使用了以下三种贴图

1.颜色贴图

这个提供了最基本的颜色,比如之前demo我们使用的。

2.法线贴图

法线贴图的本质是将一种额外的网格属性编码成RGB值存储在一张位图文件中,和光线的关系很大,光线入射到模型表面以后,发射光的方向就取决于法线方向。所以物体的明暗效果,就是由法线和光线决定的,而法线贴图,就是他们的基础色因为   漫反射下 漫反射光颜色  = 基础色*入射光颜色*cosa 。这个基础色就是法线贴图中获取的颜色。

3.高光贴图

这个高光贴图是指网格表面反光程度和反光量,和法线贴图类似。

demo来自第三章earth-shader.js 建议大家自己对照着官方demo看,这里暂时不提供下载,后续会集体放出

// Constructor
EarthApp = function()
{
	Sim.App.call(this);
}

// Subclass Sim.App
// 还是继承Sim.App
EarthApp.prototype = new Sim.App();

// Our custom initializer
/**
 * [init description]自定义初始化对象
 * @param  {[type]} param [description]
 * @return {[type]} [description]
 * @author {[name]} Daniel Wong
 * @date   {[date]} 
 * @about  {[关于]}
 */
EarthApp.prototype.init = function(param)
{
	// Call superclass init code to set up scene, renderer, default camera
	//这里已经包括了必须的scene renderer camera
	Sim.App.prototype.init.call(this, param);
	
    // Create the Earth and add it to our sim
    // 初始化地球(在下面定义)传递给SIm框架
    var earth = new Earth();
    earth.init();
    this.addObject(earth);
    
    // Let there be light!
    // 初始化光线,传递给地球
    var sun = new Sun();
    sun.init();
    this.addObject(sun);

}

// Custom Earth class
/**
 * [Earth description]自定义地球
 * @author {[name]} Daniel Wong
 * @date   {[date]} 
 * @about  {[关于]}
 */
Earth = function()
{
	Sim.Object.call(this);
}

Earth.prototype = new Sim.Object();
//以上为继承自Sim.Object 和基于类的继承有差别


/**
 * [init description]创建一个群组(在里面创建地球这个整体包含的球和云) 
 * @return {[type]} [description]
 * @author {[name]} Daniel Wong
 * @date   {[date]} 
 * @about  {[关于]}
 */
Earth.prototype.init = function()
{	
    // Create a group to contain Earth and Clouds
    var earthGroup = new THREE.Object3D();
    
    // Tell the framework about our object
    this.setObject3D(earthGroup);//这两句什么意思??

    // Add the earth globe and clouds
    // 添加球和云(两者在下文定义)到框架里面
    this.createGlobe();
    this.createClouds();
}



/**
 * [createGlobe description]创建地球的球体
 * @return {[type]} [description]
 * @author {[name]} Daniel Wong
 * @date   {[date]} 
 * @about  {[关于]}以上属于为了让逻辑清晰的封装,从这个函数开始才是干货
 * 
 */
Earth.prototype.createGlobe = function()
{
    // Create our Earth with nice texture - normal map for elevation, specular highlights
    //创建多重纹理,法线贴图,高光贴图
	var surfaceMap = THREE.ImageUtils.loadTexture( "../images/earth_surface_2048.jpg" );
	var normalMap = THREE.ImageUtils.loadTexture( "../images/earth_normal_2048.jpg" );
	var specularMap = THREE.ImageUtils.loadTexture( "../images/earth_specular_2048.jpg" );

	var shader = THREE.ShaderUtils.lib[ "normal" ],              //法线贴图着色器
	uniforms = THREE.UniformsUtils.clone( shader.uniforms );     //用之前的法线贴图着色器 创建一个uniforms对象

	uniforms[ "tNormal" ].texture = normalMap;                   //将之前创建的多重贴图 填充进uniforms对象   
	uniforms[ "tDiffuse" ].texture = surfaceMap;                 //法线贴图着色器程序至少需要一张法线贴图纹理来计算凹凸值
	uniforms[ "tSpecular" ].texture = specularMap;               

	uniforms[ "enableDiffuse" ].value = true;
	uniforms[ "enableSpecular" ].value = true;

	var shaderMaterial = new THREE.ShaderMaterial({ 
		fragmentShader: shader.fragmentShader,      //片元着色器  
		vertexShader: shader.vertexShader,          //定点着色器
		uniforms: uniforms,
		lights: true
	});

    var globeGeometry = new THREE.SphereGeometry(1, 32, 32);

    // We'll need these tangents for our shader
    //计算切线,(默认情况three.js不会为几何体计算切线),切线是用于计算法线贴图值时必要的向量值
    globeGeometry.computeTangents();
    var globeMesh = new THREE.Mesh( globeGeometry, shaderMaterial ); 
    
    // Let's work in the tilt
    // 绕x轴偏离一定角度,模拟地球黄道
    globeMesh.rotation.x = Earth.TILT;

    // Add it to our group
    // 将这个球的globeMesh添加到框架里面
    this.object3D.add(globeMesh);
	
    // Save it away so we can rotate it
    // 本身globeMesh为局部变量,无法在其它函数调用,在这里保存globeMesh到this对象,以供下面调用
    this.globeMesh = globeMesh;
}





Earth.prototype.createClouds = function()
{
	// Create our clouds
	// 创建云首先是创建一个贴图,使用兰伯特光照材质
	var cloudsMap = THREE.ImageUtils.loadTexture( "../images/earth_clouds_1024.png" );
	var cloudsMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, map: cloudsMap, transparent:true } );

    //创建云的网格
    var cloudsGeometry = new THREE.SphereGeometry(Earth.CLOUDS_SCALE, 32, 32);
	cloudsMesh = new THREE.Mesh( cloudsGeometry, cloudsMaterial );
	cloudsMesh.rotation.x = Earth.TILT;

    // Add it to our group
    this.object3D.add(cloudsMesh);
	
    // Save it away so we can rotate it
    // 存在this对象里面,供下面调用,实现一些计算(自转)
    this.cloudsMesh = cloudsMesh;
}


/**
 * [update description]update这个程序运行时,每一帧都会调用这里的内容
 * @return {[type]} [description]
 * @author {[name]} Daniel Wong
 * @date   {[date]} 2015-07-01T10:45:38+0800
 * @about  {[关于]}
 */
Earth.prototype.update = function()
{
	// "I feel the Earth move..."
	// 使地球绕y轴旋转,一定角度,因为每一帧都会调用,所以实现了自转的效果
	this.globeMesh.rotation.y += Earth.ROTATION_Y;

	// "Clouds, too..."
	//使地球绕y轴旋转,一定角度,因为每一帧都会调用,所以实现了自转的效果
	this.cloudsMesh.rotation.y += Earth.CLOUDS_ROTATION_Y;
	
	Sim.Object.prototype.update.call(this);//
}


Earth.ROTATION_Y = 0.001;
Earth.TILT = 0.41;
Earth.CLOUDS_SCALE = 1.005;
Earth.CLOUDS_ROTATION_Y = Earth.ROTATION_Y * 0.95;



// Custom Sun class
/**
 * [Sun description]创建太阳光
 * @author {[name]} Daniel Wong
 * @date   {[date]} 2015-07-01T10:48:34+0800
 * @about  {[关于]}
 */
Sun = function()
{
	Sim.Object.call(this);
}

Sun.prototype = new Sim.Object();

Sun.prototype.init = function()
{
    // Create a point light to show off the earth - set the light out back and to left a bit
    // 创建一个点光源模拟太阳
	var light = new THREE.PointLight( 0xffffff, 2, 100);
	light.position.set(-10, 0, 20);
    
    // Tell the framework about our object
    // 传递给框架
    this.setObject3D(light);    
}