一、实验目的和要求
在OpenGL观察实验的基础上,通过实现实验内容,掌握OpenGL中消隐和光照的设置,并验证课程中消隐和光照的内容。
二、实验内容和原理
使用WebGL完成已有代码,达到如下的效果(示意图是以GLUT完成)
模型尺寸参见WebGL观察实验。要求修改代码达到以下要求:
三、主要仪器设备
WebGL
Three.js
浏览器
模板工程
四、操作方法和实验步骤
1、查看老师提供的three.js的版本,在github上下载这个版本的所有文件、文档等供备用。
2、根据老师给的框架进行修改、调整,最终完成后的代码如下:
charset="UTF-8">
var renderer;
var container;
var legDist=45;//桌腿之间的距离
var directionalLight;//直射光
var directionalLightColor=0xffeedd;
var globalShininess=30;//指定高光的亮度
var isDirectionalLightChanged=true;//是否改变直射光
var isSpotLightOn=false;//开启聚光光源
var directionLightPos=[0,0,1];//平行光位置
var spotLightPos=[0,200,100];//聚光灯位置
var spotLightAngel;//聚光灯角度
var mouseX=0,mouseY=0;
var windowHalfX=window.innerWidth/2;
var windowHalfY=window.innerHeight/2;
//初始化Three绘制canvas
function initThree(){
width = document.getElementById('canvas-frame').clientWidth;
height = document.getElementById('canvas-frame').clientHeight;
renderer =new THREE.WebGLRenderer({
antialias :true
});
renderer.setSize(width, height);
document.getElementById('canvas-frame').appendChild(renderer.domElement);
renderer.setClearColor(0x000000,1.0);//清屏颜色,设置为黑
window.addEventListener('resize', onWindowResize,false);//监听窗口缩放函数
document.addEventListener('keydown', onKeyDown,false);//监听键盘按下函数
document.addEventListener('mousemove', onDocumentMouseMove,false);//鼠标事件
}
//初始化相机,函数参数与glPerspective一样
var camera;
function initCamera(){
camera =new THREE.PerspectiveCamera(45, width / height,1,10000);
camera.position.x =600;
camera.position.y =0;
camera.position.z =600;
camera.up.x =0;
camera.up.y =1;
camera.up.z =0;
camera.lookAt({
x :0,
y :0,
z :0
});
}
//初始化场景
var scene;
function initScene(){
scene =new THREE.Scene();
}
//初始化光照
var ambientLight;
var directionalLight;
var spotLight;
var pointLight;
function initLight(){
//环境光
ambientLight=new THREE.AmbientLight(0x444444);
scene.add(ambientLight);
directionalLight =new THREE.DirectionalLight(directionalLightColor,0.3);
directionalLight.position.set(directionLightPos[0], directionLightPos[1], directionLightPos[2]).normalize();//设置光源位置
scene.add( directionalLight );
//聚光灯
spotLight=new THREE.SpotLight(0xff0000,6);//
spotLight.position.set(spotLightPos[0],spotLightPos[1],spotLightPos[2]);
spotLight.angle = Math.PI /4;
scene.add(spotLight);
//点光源
pointLight=new THREE.PointLight(0xffd700,3,200);
pointLight.position.set(0,200,150);
scene.add(pointLight);
}
//定义绘制物体
function initObject(){
//桌面
var top=new THREE.CubeGeometry(200,40,200);
var material=new THREE.MeshPhongMaterial({
color:0xff0000,
specular:0xff0000,
shininess:30
});
mesh=new THREE.Mesh(top,material);
mesh.position.set(0,90,0);//这个位置是取mesh的体心
scene.add(mesh);
//leg1
var leg1=new THREE.CubeGeometry(50,150,50);
var material1=new THREE.MeshPhongMaterial({
color:0xffff00,
specular:0xffff00,
shininess:globalShininess
});
mesh1=new THREE.Mesh(leg1,material1);
mesh1.position.set(-legDist,0,-legDist);
scene.add(mesh1);
//leg2
var leg2=new THREE.CubeGeometry(50,150,50);
var material2=new THREE.MeshPhongMaterial({
color:0x4876ff,
specular:0x4876ff,
shininess:globalShininess
});
mesh2=new THREE.Mesh(leg2,material2);
mesh2.position.set(-legDist,0,legDist);
scene.add(mesh2);
//leg3
var leg3=new THREE.CubeGeometry(50,150,50);
var material3=new THREE.MeshPhongMaterial({
color:0x7fff00,
specular:0x7fff00,
shininess:globalShininess
});
mesh3=new THREE.Mesh(leg3,material3);
mesh3.position.set(legDist,0,-legDist);
scene.add(mesh3);
//leg4
var leg4=new THREE.CubeGeometry(50,150,50);
var material4=new THREE.MeshPhongMaterial({
color:0x7fffd4,
specular:0x7fffd4,
shininess:globalShininess
});
mesh4=new THREE.Mesh(leg4,material4);
mesh4.position.set(legDist,0,legDist);
scene.add(mesh4);
//obj
var onProgress =function( xhr ){
if( xhr.lengthComputable ){
var percentComplete = xhr.loaded / xhr.total *100;
console.log( Math.round(percentComplete,2)+'% downloaded');
}
};
var onError =function( xhr ){}
THREE.Loader.Handlers.add(/\.dds$/i,new THREE.DDSLoader());
var loader =new THREE.OBJMTLLoader();
loader.load('obj/teapot.obj','obj/teapot.mtl',function( object ){
object.position.y=110;//桌腿的一半加上桌面的高度,应该是115但是实际看起来茶壶还是有点漂浮...
//所以为了美观设置成110吧^_^
scene.add( object );
}, onProgress, onError );
}
//键盘操作
function onKeyDown(event){
console.log(event.keyCode);
//具体操作
if(event.keyCode==88){//改变直射光颜色,x
isDirectionalLightChanged=!isDirectionalLightChanged;
}
//移动场景中的光源
elseif(event.keyCode==65){//左,a
directionLightPos[0]-=0.5;
}
elseif(event.keyCode==68){//右,d
directionLightPos[0]+=0.5;
}
elseif(event.keyCode==87){//上,w
directionLightPos[1]+=0.5;
}
elseif(event.keyCode==83){//下,s
directionLightPos[1]-=0.5;
}
elseif(event.keyCode==90){//远,z
directionLightPos[2]+=0.5;
}
elseif(event.keyCode==67){//近,c
directionLightPos[2]-=0.5;
}
elseif(event.keyCode==81){//反射系数变大,q
globalShininess+=10;
}
elseif(event.keyCode==69){//反射系数变小,e
globalShininess-=10;
if(globalShininess<=0){
globalShininess=0;
}
}
elseif(event.keyCode==85){//聚光光源开关
isSpotLightOn=!-isSpotLightOn;
}
//聚光光缘移动
elseif(event.keyCode==74){//左,j
spotLightPos[0]-=10;
}
elseif(event.keyCode==76){//右,l
spotLightPos[0]+=10;
}
elseif(event.keyCode==73){//上,i
spotLightPos[2]-=10;
}
elseif(event.keyCode==75){//下,k
spotLightPos[2]+=10;
}
//聚光角度改变
elseif(event.keyCode==78){//角度变大,n
spotLightPos[1]+=10;
}
elseif(event.keyCode==77){//角度变小,m
spotLightPos[1]-=10;
}
//animate();
}
//鼠标事件
function onDocumentMouseMove( event ){
mouseX =( event.clientX - windowHalfX )/2;
mouseY =( event.clientY - windowHalfY )/2;
}
//窗口缩放
function onWindowResize(){
windowHalfX = window.innerWidth /2;
windowHalfY = window.innerHeight /2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//绘制函数
function animate(){
camera.position.x +=( mouseX - camera.position.x )*.5;
camera.position.y +=(- mouseY - camera.position.y )*.5;
camera.lookAt( scene.position );
if(isDirectionalLightChanged==true){
directionalLight.color.setHex(0xffeedd);
}
else{
directionalLight.color.setHex(0x00ff00);//改变为蓝色
}
if(isSpotLightOn){
spotLight.color.setHex(0x00ff00);
}
else{
spotLight.color.setHex(0x000000);
}
directionalLight.position.set(directionLightPos[0], directionLightPos[1], directionLightPos[2]).normalize();//设置平行光位置
spotLight.position.set(spotLightPos[0],spotLightPos[1],spotLightPos[2]);//设置聚光灯位置
renderer.clear();
renderer.render(scene, camera);
requestAnimationFrame( animate );
}
//主函数
function threeStart(){
initThree();
initCamera();
initScene();
initLight();
initObject();
animate();
}
onload="threeStart();">
在进行这步之前,需要把合适的js脚本添加到工程目录下,我添加的脚本如下:
3、运行,反复测试效果,期间需要调整光源的位置、颜色等,让画面看起来比较舒服。
4、如果没有问题,交给助教验收,验收通过即实验结束。
五、实验数据记录和处理
根据实验要求,记录了以下情况:
(一开始的场景) (高光)
(光源左移) (光源右移)
(光源上移) (光源下移)
(光源前移) (光源后移)
(改变光源颜色) (开启聚光灯,灯左移)
(聚光灯右移) (聚光灯上移)
(聚光灯下移) (聚光灯角度变大)
(聚光灯角度变小)
六、实验结果与分析
七、讨论、心得
(1)如何加载obj
做实验之前去网上搜索了相关的方法,大家基本是用OBJLoader和MTLLoader两个函数配合实现的,但那时查看老师给的版本发现是r73后找到了一个更快捷的方法,叫做OBJMTLLoader,它可以一次性搞定obj和mtl的加载,这样就不用去调两个函数了。但是这个方法在最新版本r88中已经被移除了。
(2)键盘事件响应过慢
一开始发现这个问题还以为是WebGL自己的问题,后来发现是animate函数在处理键盘事件的函数中又被调用了一次,重复调用造成了按键卡顿,解决办法是删去多余的animate()。
(3)浏览器的支持问题
一开始我是在chrome上做的实验,用OBJMTLLoader的时候发现它给我报了这个错:
Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
查资料以后找到了解决办法,需要自己去设置浏览器的某些选项,我嫌麻烦就直接换成了FireFox,问题也就顺利解决了。