您的位置:首页 > Web前端 > JavaScript

[webGL学习]基于three.js构建WebGL实例第五讲

2016-10-11 17:20 591 查看


今天,我们将继续学习webgl(three.js)这门课程,今天我们将向您展示如何以三种不同的方式为您的场景创建一个美丽的环境(天空立体景象):立方天空盒 侧面),球形天空盒(单周围纹理)和球形着色器天空盒(无纹理)。 我们会用到下面一些比较特殊的属性:反射,折射和类似soapbubble的对象。

一般结构

现在我们可以开始,首先,定义一般结构:

var lesson5 = {
scene: null,
camera: null,
renderer: null,
container: null,
controls: null,
clock: null,
stats: null,

init: function() { // Initialization

// create main scene
this.scene = new THREE.Scene();

var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight;

// prepare camera
var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 1, FAR = 1000;
this.camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
this.scene.add(this.camera);
this.camera.position.set(0, 30, 150);
this.camera.lookAt(new THREE.Vector3(0,0,0));

// prepare renderer
this.renderer = new THREE.WebGLRenderer({antialias:true, alpha: false});
this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
this.renderer.setClearColor(0xffffff);

this.renderer.shadowMapEnabled = true;
this.renderer.shadowMapSoft = true;

// prepare container
this.container = document.createElement('div');
document.body.appendChild(this.container);
this.container.appendChild(this.renderer.domElement);

// events
THREEx.WindowResize(this.renderer, this.camera);

// prepare controls (OrbitControls)
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.target = new THREE.Vector3(0, 0, 0);
this.controls.maxDistance = 700;

// prepare clock
this.clock = new THREE.Clock();

// prepare stats
this.stats = new Stats();
this.stats.domElement.style.position = 'absolute';
this.stats.domElement.style.left = '50px';
this.stats.domElement.style.bottom = '50px';
this.stats.domElement.style.zIndex = 1;
this.container.appendChild( this.stats.domElement );

// add point light
var spLight = new THREE.PointLight(0xffffff, 1.75, 1000);
spLight.position.set(-100, 200, 200);
this.scene.add(spLight);

// add simple cube
var cube = new THREE.Mesh( new THREE.CubeGeometry(50, 10, 50), new THREE.MeshLambertMaterial({color:0xffffff * Math.random()}) );
cube.position.set(0, 0, 0);
this.scene.add(cube);

// add custom objects
// .....
}
};

// Animate the scene
function animate() {
requestAnimationFrame(animate);
render();
update();
}

// Update controls and stats
function update() {
lesson5.controls.update(lesson5.clock.getDelta());
lesson5.stats.update();
}

// Render the scene
function render() {
if (lesson5.renderer) {
lesson5.renderer.render(lesson5.scene, lesson5.camera);
}
}

// Initialize lesson on page load
function initializeLesson() {
lesson5.init();
animate();
}

if (window.addEventListener)
window.addEventListener('load', initializeLesson, false);
else if (window.attachEvent)
window.attachEvent('onload', initializeLesson);
else window.onload = initializeLesson;


这是非常常见的结构,添加了所有的一般元素,如:scene本身,camera,render,controls,light和stats元素。 现在我们将要开始描述每个天空盒类型。

Part 1: Skyboxes

1. Cubic Skybox (textured)

drawSimpleSkybox: function() {
// define path and box sides images
var path = 'skybox/1/';
var sides = [ path + 'sbox_px.jpg', path + 'sbox_nx.jpg', path + 'sbox_py.jpg', path + 'sbox_ny.jpg', path + 'sbox_pz.jpg', path + 'sbox_nz.jpg' ];

// load images
var scCube = THREE.ImageUtils.loadTextureCube(sides);
scCube.format = THREE.RGBFormat;

// prepare skybox material (shader)
var skyShader = THREE.ShaderLib["cube"];
skyShader.uniforms["tCube"].value = scCube;
var skyMaterial = new THREE.ShaderMaterial( {
fragmentShader: skyShader.fragmentShader, vertexShader: skyShader.vertexShader,
uniforms: skyShader.uniforms, depthWrite: false, side: THREE.BackSide
});

// create Mesh with cube geometry and add to the scene
var skyBox = new THREE.Mesh(new THREE.CubeGeometry(500, 500, 500), skyMaterial);
skyMaterial.needsUpdate = true;

this.scene.add(skyBox);
}


最简单的 - 是创建基本的立方天空盒子。 在three.js的utils类中有一个特殊的函数来加载图像集合:ImageUtils :: loadTextureCube。 然后,我们使用THREE.ShaderLib为我们下面的立方天空盒创建ShaderMaterial(这个使用CubeGeometry类)。

2. Spherical skybox (textured)

除了标准的方式,天空场景可以是球面的,在这种情况下,我们可以只使用一个球面纹理的来修饰天空:

drawSphericalSkybox: function() {
// prepare ShaderMaterial
var uniforms = {
texture: { type: 't', value: THREE.ImageUtils.loadTexture('skybox/2/skybox.jpg') }
};
var skyMaterial = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById('sky-vertex').textContent, fragmentShader: document.getElementById('sky-fragment').textContent
});

// create Mesh with sphere geometry and add to the scene
var skyBox = new THREE.Mesh(new THREE.SphereGeometry(250, 60, 40), skyMaterial);
skyBox.scale.set(-1, 1, 1);
skyBox.eulerOrder = 'XZY';
skyBox.renderDepth = 500.0;

this.scene.add(skyBox);
}


注意,为了构建它,我们使用特殊着色器:天顶点和天空碎片:

<!-- skybox shaders -->
<script type="application/x-glsl" id="sky-vertex">
varying vec2 vUV;

void main() {
vUV = uv;
vec4 pos = vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewMatrix * pos;
}
</script>

<script type="application/x-glsl" id="sky-fragment">
uniform sampler2D texture;
varying vec2 vUV;

void main() {
vec4 sample = texture2D(texture, vUV);
gl_FragColor = vec4(sample.xyz, sample.w);
}
</script>
<!-- /skybox shaders -->


因此,我们的球形天空盒有一个类似的结构,但我们使用另一个几何(SphereGeometry)和新的着色材料.

3. Spherical skybox (non textured)

有时,清晰的天空就足够了,在这种情况下,我们不需要使用额外的图像来构建天空盒。 在这里,你可以找到我们如何避免使用纹理来构建漂亮的球形天空盒与渐变。 首先,我们需要添加两个新的着色器:

<!-- skybox shaders -->
<script type="x-shader/x-vertex" id="sky-vertex">
varying vec3 vWorldPosition;

void main() {
vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
vWorldPosition = worldPosition.xyz;

gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>

<script type="x-shader/x-fragment" id="sky-fragment">
uniform vec3 topColor;
uniform vec3 bottomColor;
uniform float offset;
uniform float exponent;

varying vec3 vWorldPosition;

void main() {
float h = normalize( vWorldPosition + offset ).y;
gl_FragColor = vec4( mix( bottomColor, topColor, max( pow( h, exponent ), 0.0 ) ), 1.0 );
}
</script>
<!-- /skybox shaders -->


然后,实现基于渐变的skybox:

drawShaderSkybox: function() {

// prepare ShaderMaterial without textures
var vertexShader = document.getElementById('sky-vertex').textContent, fragmentShader = document.getElementById('sky-fragment').textContent;
var uniforms = {
topColor: {type: "c", value: new THREE.Color(0x0055ff)}, bottomColor: {type: "c", value: new THREE.Color(0xffffff)},
offset: {type: "f", value: 50}, exponent: {type: "f", value: 0.6}
}
var skyMaterial = new THREE.ShaderMaterial({vertexShader: vertexShader, fragmentShader: fragmentShader, uniforms: uniforms, side: THREE.BackSide, fog: false});

// create Mesh with sphere geometry and add to the scene
var skyBox = new THREE.Mesh( new THREE.SphereGeometry(250, 60, 40), skyMaterial);

this.scene.add(skyBox);
}


在例子中 - 我们没有使用图像,我们只使用两种颜色的渐变:顶部和底部的颜色。

Part 2: Additional objects

正如你可能已经注意到 - 这里,使用各种视觉效果:反射,折射和气泡。 在这一节,将向大家展示一下具体的实现过程。

1. Reflection

你可以使用这个方法(事实上 - 函数)来构建反射面:

drawReflectingObjects: function() {
// Object 1: rectangle

// create additional camera
this.mCubeCamera = new THREE.CubeCamera(0.1, 1000, 1000); // near, far, cubeResolution
this.scene.add(this.mCubeCamera);

// create mirror material and mesh
var mirrorCubeMaterial = new THREE.MeshBasicMaterial( { envMap: this.mCubeCamera.renderTarget, side: THREE.DoubleSide } );
this.mCube = new THREE.Mesh( new THREE.CubeGeometry(100, 100, 5, 1, 1, 1), mirrorCubeMaterial);
this.mCube.position.set(-50, 0, -150);
this.mCubeCamera.position = this.mCube.position;
this.mCubeCamera.lookAt(new THREE.Vector3(0, 0, 0));
this.scene.add(this.mCube);

// Object 2: sphere

// create additional camera
this.mSphereCamera = new THREE.CubeCamera(0.1, 1000, 100);
this.scene.add(this.mSphereCamera);

// create mirror material and mesh
var mirrorSphereMaterial = new THREE.MeshBasicMaterial( { envMap: this.mSphereCamera.renderTarget, side: THREE.DoubleSide } );
this.mSphere = new THREE.Mesh( new THREE.SphereGeometry(50, 32, 32), mirrorSphereMaterial );
this.mSphere.position.set(50, 0, -150);
this.mSphereCamera.position = this.mSphere.position;
this.mSphereCamera.lookAt(new THREE.Vector3(0, 0, 0));
this.scene.add(this.mSphere);
}


一般来说 - 这很容易实现 - 我们使用两个对象(矩形和球体)与基本材料。 但这里是一个小技巧 - 我们在我们的材料中使用了envMap。 此属性允许设置材料的环境贴图。 刚刚上面我们创造了两个摄像机,并把它放在相同的位置,我们的立方体和球体。 这就是为什么他们反映。 然而,有一个时刻 - 每次我们移动我们的主要相机 - 我们需要更新我们的相机,因此我们需要添加以下代码到’render’这个函数:

// Render the scene
function render() {
if (lesson5.renderer) {

// update reflecting objects
lesson5.mCube.visible = false;
lesson5.mCubeCamera.updateCubeMap(lesson5.renderer, lesson5.scene);
lesson5.mCube.visible = true;

lesson5.mSphere.visible = false;
lesson5.mSphereCamera.updateCubeMap(lesson5.renderer, lesson5.scene);
lesson5.mSphere.visible = true;

lesson5.renderer.render(lesson5.scene, lesson5.camera);
}
}


2. Refraction

你可以使用这个方法(事实上 - 函数)来创建折射对象:

drawRefractingObject: function() {

// create additional camera
this.rSphereCamera = new THREE.CubeCamera(0.1, 1000, 1000);
this.scene.add(this.rSphereCamera);
this.rSphereCamera.renderTarget.mapping = new THREE.CubeRefractionMapping();

// create refracting material and spherical mesh
var rMaterial = new THREE.MeshBasicMaterial({
color: 0xffffdd,
envMap: this.rSphereCamera.renderTarget,
refractionRatio: 0.995,
reflectivity: 0.5
});

this.rSphere = new THREE.Mesh( new THREE.SphereGeometry(40, 32, 32), rMaterial);
this.rSphere.position.set(0, 0, 100);
this.rSphereCamera.position = this.rSphere.position;
this.scene.add(this.rSphere);
}


我们使用几乎相同的方法,我们用于反射。 我们只为我们的材料添加了两个新属性:refractionRatio和reflectivity。 样式为我们的球体添加折射效果,现在看起来像放大镜。 请记住,我们需要在’渲染’功能中更新我们的相机:

// Render the scene
function render() {
if (lesson5.renderer) {

// update refracting object
lesson5.rSphere.visible = false;
lesson5.rSphereCamera.updateCubeMap(lesson5.renderer, lesson5.scene);
lesson5.rSphere.visible = true;

lesson5.renderer.render(lesson5.scene, lesson5.camera);
}
}


3. Bubble

<!-- bubble shaders -->
<script type="x-shader/x-vertex" id="bubble-vertex">
uniform float mRefractionRatio;
uniform float mBias;
uniform float mScale;
uniform float mPower;

varying vec3 vReflect;
varying vec3 vRefract[3];
varying float vReflectionFactor;

void main() {

vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
vec4 worldPosition = modelMatrix * vec4( position, 1.0 );

vec3 worldNormal = normalize( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );

vec3 I = worldPosition.xyz - cameraPosition;

vReflect = reflect( I, worldNormal );
vRefract[0] = refract( normalize( I ), worldNormal, mRefractionRatio );
vRefract[1] = refract( normalize( I ), worldNormal, mRefractionRatio * 0.99 );
vRefract[2] = refract( normalize( I ), worldNormal, mRefractionRatio * 0.98 );
vReflectionFactor = mBias + mScale * pow( 1.0 + dot( normalize( I ), worldNormal ), mPower );

gl_Position = projectionMatrix * mvPosition;
}
</script>

<script type="x-shader/x-fragment" id="bubble-fragment">
uniform samplerCube tCube;
varying vec3 vReflect;
varying vec3 vRefract[3];
varying float vReflectionFactor;

void main() {
vec4 reflectedColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );
vec4 refractedColor = vec4( 1.0 );

refractedColor.r = textureCube( tCube, vec3( -vRefract[0].x, vRefract[0].yz ) ).r;
refractedColor.g = textureCube( tCube, vec3( -vRefract[1].x, vRefract[1].yz ) ).g;
refractedColor.b = textureCube( tCube, vec3( -vRefract[2].x, vRefract[2].yz ) ).b;

gl_FragColor = mix( refractedColor, reflectedColor, clamp( vReflectionFactor, 0.0, 1.0 ) );
}
</script>
<!-- /bubble shaders -->


这里是新的着色材料与THREE.Cube相机为我们的对象:

drawBubbleObject: function() {

// create additional camera
this.bSphereCamera = new THREE.CubeCamera(0.1, 1000, 1000);
this.scene.add(this.bSphereCamera);

// prepare custom ShaderMaterial
var uniforms =  {
"mRefractionRatio": { type: "f", value: 1.02 },
"mBias":     { type: "f", value: 0.1 },
"mPower":    { type: "f", value: 2.0 },
"mScale":    { type: "f", value: 1.0 },
"tCube":     { type: "t", value: this.bSphereCamera.renderTarget } //  textureCube }
};

// create custom material for the shader
var customMaterial = new THREE.ShaderMaterial({
uniforms:       uniforms,
vertexShader:   document.getElementById('bubble-vertex').textContent,
fragmentShader: document.getElementById('bubble-fragment').textContent
});

// create spherical mesh
this.bSphere = new THREE.Mesh( new THREE.SphereGeometry(50, 32, 32), customMaterial);
this.bSphere.position.set(-75, 0, 0);
this.scene.add(this.bSphere);

this.bSphereCamera.position = this.bSphere.position;
}


再次,我们需要更新您的额外的相机所有的时间:

// Render the scene
function render() {
if (lesson5.renderer) {

// update bubble object
lesson5.bSphere.visible = false;
lesson5.bSphereCamera.updateCubeMap(lesson5.renderer, lesson5.scene);
lesson5.bSphere.visible = true;

lesson5.renderer.render(lesson5.scene, lesson5.camera);
}
}


结束

未完继续

源码下载请关注我的微信公众号

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息