用100行代Three.js代码创建一座城市
2017-09-29 15:29
344 查看
翻译有删改
原文链接:
http://learningthreejs.com/blog/2013/08/02/how-to-do-a-procedural-city-in-100lines/
算法评价
在深入细节之前先有一个全局的概念总是好的。该算法实现的整座城市都是动态建立的,而不是实现下载好的模型。算法写的非常优雅,创建一个3D城市仅仅用了100行代码。概括来讲:每栋建筑都是一个立方体,且它们的大小和位置随机。
从性能表现角度来说,所有的建筑都融合成一个单一的几何形状,有着单一的材质。没有材质上的变换和单一的绘图调用使得程序非常高效。
为了提高真实度,我们用一个简单的办法来模拟环境光的遮挡效果——使用vertexColor。在城市中,街道层有来自其他建筑物的阴影。所以建筑物的底部比顶部更暗。我们能通过设置vertexColor,使得建筑物底部顶点比顶部的颜色更暗,从而再现这个效果。
开始
我们一步步分析这100行代码:首先,我们为建筑物制造基本的几何形状;然后,我们使用这个几何形状来确定在城市的哪里放置建筑物,使用vertexColor来实现环境光遮挡的效果;然后,我们整合所有的建筑物来形成一个城市。因此,绘制整座城市只需要一个单一的绘制调用。最后,我们详细讲一下在渐进生成过程中建筑物的纹理。
让我们开始吧~!
为建筑物制造基本的几何形状
我们创建一个简单的立方体作为基本形状。
[javascript] view
plain copy
var geometry = new THREE.CubeGeometry( 1, 1, 1 );
我们将立方体的中轴点从中心变到底部。
[javascript] view
plain copy
geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.5, 0 ) );
然后移除底部的面。这是一个优化的步骤,因为立方体底部的面永远不会被看到,所以可以移除。
[javascript] view
plain copy
geometry.faces.splice( 3, 1 );
现在我们为顶部的面修整UV贴图。将它们放到单一坐标(0,0)中。这样屋顶和地板颜色就一样了。因为建筑物的每个面都用一张贴图,所以调用绘制函数一次就好。
[javascript] view
plain copy
geometry.faceVertexUvs[0][2][0].set( 0, 0 );
geometry.faceVertexUvs[0][2][1].set( 0, 0 );
geometry.faceVertexUvs[0][2][2].set( 0, 0 );
geometry.faceVertexUvs[0][2][3].set( 0, 0 );
好啦~现在我们有了单一建筑物的几何形状,接下来我们用建筑物来组合出一座城市吧~!
在城市的哪里放置建筑物
好吧,实际上我们是随机摆放它们的。虽然这样它们会发生冲突,但是在一个较低的位置漫游时看起来还好。
[javascript] view
plain copy
buildingMesh.position.x = Math.floor( Math.random() * 200 - 100 ) * 10;
buildingMesh.position.z = Math.floor( Math.random() * 200 - 100 ) * 10;
然后给Y轴加一个随机的旋转。
[javascript] view
plain copy
buildingMesh.rotation.y = Math.random()*Math.PI*2;
然后我们通过改变mesh.scale来改变建筑物的大小。首先是宽度和深度。
[javascript] view
plain copy
buildingMesh.scale.x = Math.random()*Math.random()*Math.random()*Math.random() * 50 + 10;
buildingMesh.scale.z = buildingMesh.scale.x
然后是高度。
[javascript] view
plain copy
buildingMesh.scale.y = (Math.random() * Math.random() * Math.random() * buildingMesh.scale.x) * 8 + 8;
在这里很多个Math.random()的连乘改变了结果的统计分布,使其更接近于0.现在,建筑物的位置、旋转和大小已经都设置好了。接下来设置它们的颜色和阴影仿真。
使用VertexColor模拟环境光遮挡
在graphic programming里面,环境光遮挡(ambientocclusion)可以被应用到很多个方面。
首先我们分别定义接收光源部分和阴影部分的基础色。这对于每个建筑物都是常量。
[javascript] view
plain copy
var light = new THREE.Color( 0xffffff )
var shadow = new THREE.Color( 0x303050 )
接下来我们加一些随机值作为每个建筑物的变化色。
[javascript] view
plain copy
var value = 1 - Math.random() * Math.random();
var baseColor = new THREE.Color().setRGB( value + Math.random() * 0.1, value, value + Math.random() * 0.1 );
现在我们需要给每个面的每个顶点分配.vertexColor。顶部面给baseColor,旁边的面给baseColor乘以顶部顶点的light和底部顶点的shaddow。以此来做简单的环境光遮挡效果。
[javascript] view
plain copy
// set topColor/bottom vertexColors as adjustement of baseColor
var topColor = baseColor.clone().multiply( light );
var bottomColor = baseColor.clone().multiply( shadow );
// set .vertexColors for each face
var geometry = buildingMesh.geometry;
for ( var j = 0, jl = geometry.faces.length; j < jl; j ++ ) {
if ( j === 2 ) {
// set face.vertexColors on root face
geometry.faces[ j ].vertexColors = [ baseColor, baseColor, baseColor, baseColor ];
} else {
// set face.vertexColors on sides faces
geometry.faces[ j ].vertexColors = [ topColor, bottomColor, bottomColor, topColor ];
}
}
现在单独的建筑物已经完全设置好了~!
用所有的建筑物组合成一座城市
为了制造我们的城市,我们需要整合20000个建筑物。所以我们用一个循环并且把循环中的建筑物都做以上处理。因为现在所有的建筑物都使用同样的材质,所以我们准备将它们整合到一个几何形状里。
[javascript] view
plain copy
var cityGeometry= new THREE.Geometry();
for( var i = 0; i < 20000; i ++ ){
// set the position/rotation/color the building in the city
// ...
// merge it with cityGeometry - very important for performance
THREE.GeometryUtils.merge( cityGeometry, buildingMesh );
}
现在我们得到了城市的一整个几何形体,然后为这个大几何形体创建一个网格。
[javascript] view
plain copy
// build the mesh
var material = new THREE.MeshLambertMaterial({
map : texture,
vertexColors : THREE.VertexColors
});
var mesh = new THREE.Mesh(cityGeometry, material );
这个网格就是整座城市的模型。太棒啦~!接下来是最后一步,我们会讲解如何制作贴图。
建筑物贴图的渐进生成(procedural generation)
这里我们想要生成每个建筑物侧面的纹理。简单的说,这会展示出楼层的真实感和多样性。所以它在窗户行和楼层行之间交替进行。窗户行是带着微小噪音的黑色来模拟每间房间的光线变化。然后我们小心地将纹理升级以避免滤波。
首先创建一个小的canvas画布。
[javascript] view
plain copy
var canvas = document.createElement( 'canvas' );
canvas.width = 32;
canvas.height = 64;
var context = canvas.getContext( '2d' );
然后染成白色。
[javascript] view
plain copy
context.fillStyle = '#ffffff';
context.fillRect( 0, 0, 32, 64 );
现在我们开始在这个白色的表面。我们准备在上面绘制地板。一个窗户行,一个地板行然后进行循环。实际上,当表面已经是白色的时候,我们只需要绘制窗户行就可以了。为了绘制窗户行,我们要加一些随机值以模拟窗户中的光线变化。
[javascript] view
plain copy
for( var y = 2; y < 64; y += 2 ){
for( var x = 0; x < 32; x += 2 ){
var value = Math.floor( Math.random() * 64 );
context.fillStyle = 'rgb(' + [value, value, value].join( ',' ) + ')';
context.fillRect( x, y, 2, 1 );
}
}
现在我们得到的纹理很小,为了放大后不模糊,我们关闭了.imageSmoothedEnabled效果。下面是代码:首先创建一个1024*512的大画布。
[javascript] view
plain copy
var canvas2 = document.createElement( 'canvas' );
canvas2.width = 512;
canvas2.height = 1024;
var context = canvas2.getContext( '2d' );
然后关闭平滑。
[javascript] view
plain copy
context.imageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;
现在把小的画布拷贝到大的里面。
[javascript] view
plain copy
context.drawImage( canvas, 0, 0, canvas2.width, canvas2.height );
然后我们需要做的就是创建THREE.Texture。将anisotropie设置成一个较大的值以得到更好的效果。
[javascript] view
plain copy
var texture = new THREE.Texture( generateTexture() );
texture.anisotropy = renderer.getMaxAnisotropy();
texture.needsUpdate = true;
完整代码
[javascript] view
plain copy
// build the base geometry for each building
var geometry = new THREE.CubeGeometry( 1, 1, 1 );
// translate the geometry to place the pivot point at the bottom instead of the center
geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.5, 0 ) );
// get rid of the bottom face - it is never seen
geometry.faces.splice( 3, 1 );
geometry.faceVertexUvs[0].splice( 3, 1 );
// change UVs for the top face
// - it is the roof so it wont use the same texture as the side of the building
// - set the UVs to the single coordinate 0,0. so the roof will be the same color
// as a floor row.
geometry.faceVertexUvs[0][2][0].set( 0, 0 );
geometry.faceVertexUvs[0][2][1].set( 0, 0 );
geometry.faceVertexUvs[0][2][2].set( 0, 0 );
geometry.faceVertexUvs[0][2][3].set( 0, 0 );
// buildMesh
var buildingMesh= new THREE.Mesh( geometry );
// base colors for vertexColors. light is for vertices at the top, shaddow is for the ones at the bottom
var light = new THREE.Color( 0xffffff )
var shadow = new THREE.Color( 0x303050 )
var cityGeometry= new THREE.Geometry();
for( var i = 0; i < 20000; i ++ ){
// put a random position
buildingMesh.position.x = Math.floor( Math.random() * 200 - 100 ) * 10;
buildingMesh.position.z = Math.floor( Math.random() * 200 - 100 ) * 10;
// put a random rotation
buildingMesh.rotation.y = Math.random()*Math.PI*2;
// put a random scale
buildingMesh.scale.x = Math.random() * Math.random() * Math.random() * Math.random() * 50 + 10;
buildingMesh.scale.y = (Math.random() * Math.random() * Math.random() * buildingMesh.scale.x) * 8 + 8;
buildingMesh.scale.z = buildingMesh.scale.x
// establish the base color for the buildingMesh
var value = 1 - Math.random() * Math.random();
var baseColor = new THREE.Color().setRGB( value + Math.random() * 0.1, value, value + Math.random() * 0.1 );
// set topColor/bottom vertexColors as adjustement of baseColor
var topColor = baseColor.clone().multiply( light );
var bottomColor = baseColor.clone().multiply( shadow );
// set .vertexColors for each face
var geometry = buildingMesh.geometry;
for ( var j = 0, jl = geometry.faces.length; j < jl; j ++ ) {
if ( j === 2 ) {
// set face.vertexColors on root face
geometry.faces[ j ].vertexColors = [ baseColor, baseColor, baseColor, baseColor ];
} else {
// set face.vertexColors on sides faces
geometry.faces[ j ].vertexColors = [ topColor, bottomColor, bottomColor, topColor ];
}
}
// merge it with cityGeometry - very important for performance
THREE.GeometryUtils.merge( cityGeometry, buildingMesh );
}
// generate the texture
var texture = new THREE.Texture( generateTexture() );
texture.anisotropy = renderer.getMaxAnisotropy();
texture.needsUpdate = true;
// build the mesh
var material = new THREE.MeshLambertMaterial({
map : texture,
vertexColors : THREE.VertexColors
});
var cityMesh = new THREE.Mesh(cityGeometry, material );
function generateTexture() {
// build a small canvas 32x64 and paint it in white
var canvas = document.createElement( 'canvas' );
canvas.width = 32;
canvas.height = 64;
var context = canvas.getContext( '2d' );
// plain it in white
context.fillStyle = '#ffffff';
context.fillRect( 0, 0, 32, 64 );
// draw the window rows - with a small noise to simulate light variations in each room
for( var y = 2; y < 64; y += 2 ){
for( var x = 0; x < 32; x += 2 ){
var value = Math.floor( Math.random() * 64 );
context.fillStyle = 'rgb(' + [value, value, value].join( ',' ) + ')';
context.fillRect( x, y, 2, 1 );
}
}
// build a bigger canvas and copy the small one in it
// This is a trick to upscale the texture without filtering
var canvas2 = document.createElement( 'canvas' );
canvas2.width = 512;
canvas2.height = 1024;
var context = canvas2.getContext( '2d' );
// disable smoothing
context.imageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;
// then draw the image
context.drawImage( canvas, 0, 0, canvas2.width, canvas2.height );
// return the just built canvas2
return canvas2;
}
threex.proceduralcity扩展
这段代码被集成到一个易于复用的threex包里面:threex.proceduralcity。使用起来非常简单。
[javascript] view
plain copy
var city = new THREEx.ProceduralCity()
scene.add(city)
原文链接:
http://learningthreejs.com/blog/2013/08/02/how-to-do-a-procedural-city-in-100lines/
算法评价
在深入细节之前先有一个全局的概念总是好的。该算法实现的整座城市都是动态建立的,而不是实现下载好的模型。算法写的非常优雅,创建一个3D城市仅仅用了100行代码。概括来讲:每栋建筑都是一个立方体,且它们的大小和位置随机。
从性能表现角度来说,所有的建筑都融合成一个单一的几何形状,有着单一的材质。没有材质上的变换和单一的绘图调用使得程序非常高效。
为了提高真实度,我们用一个简单的办法来模拟环境光的遮挡效果——使用vertexColor。在城市中,街道层有来自其他建筑物的阴影。所以建筑物的底部比顶部更暗。我们能通过设置vertexColor,使得建筑物底部顶点比顶部的颜色更暗,从而再现这个效果。
开始
我们一步步分析这100行代码:首先,我们为建筑物制造基本的几何形状;然后,我们使用这个几何形状来确定在城市的哪里放置建筑物,使用vertexColor来实现环境光遮挡的效果;然后,我们整合所有的建筑物来形成一个城市。因此,绘制整座城市只需要一个单一的绘制调用。最后,我们详细讲一下在渐进生成过程中建筑物的纹理。
让我们开始吧~!
为建筑物制造基本的几何形状
我们创建一个简单的立方体作为基本形状。
[javascript] view
plain copy
var geometry = new THREE.CubeGeometry( 1, 1, 1 );
我们将立方体的中轴点从中心变到底部。
[javascript] view
plain copy
geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.5, 0 ) );
然后移除底部的面。这是一个优化的步骤,因为立方体底部的面永远不会被看到,所以可以移除。
[javascript] view
plain copy
geometry.faces.splice( 3, 1 );
现在我们为顶部的面修整UV贴图。将它们放到单一坐标(0,0)中。这样屋顶和地板颜色就一样了。因为建筑物的每个面都用一张贴图,所以调用绘制函数一次就好。
[javascript] view
plain copy
geometry.faceVertexUvs[0][2][0].set( 0, 0 );
geometry.faceVertexUvs[0][2][1].set( 0, 0 );
geometry.faceVertexUvs[0][2][2].set( 0, 0 );
geometry.faceVertexUvs[0][2][3].set( 0, 0 );
好啦~现在我们有了单一建筑物的几何形状,接下来我们用建筑物来组合出一座城市吧~!
在城市的哪里放置建筑物
好吧,实际上我们是随机摆放它们的。虽然这样它们会发生冲突,但是在一个较低的位置漫游时看起来还好。
[javascript] view
plain copy
buildingMesh.position.x = Math.floor( Math.random() * 200 - 100 ) * 10;
buildingMesh.position.z = Math.floor( Math.random() * 200 - 100 ) * 10;
然后给Y轴加一个随机的旋转。
[javascript] view
plain copy
buildingMesh.rotation.y = Math.random()*Math.PI*2;
然后我们通过改变mesh.scale来改变建筑物的大小。首先是宽度和深度。
[javascript] view
plain copy
buildingMesh.scale.x = Math.random()*Math.random()*Math.random()*Math.random() * 50 + 10;
buildingMesh.scale.z = buildingMesh.scale.x
然后是高度。
[javascript] view
plain copy
buildingMesh.scale.y = (Math.random() * Math.random() * Math.random() * buildingMesh.scale.x) * 8 + 8;
在这里很多个Math.random()的连乘改变了结果的统计分布,使其更接近于0.现在,建筑物的位置、旋转和大小已经都设置好了。接下来设置它们的颜色和阴影仿真。
使用VertexColor模拟环境光遮挡
在graphic programming里面,环境光遮挡(ambientocclusion)可以被应用到很多个方面。
首先我们分别定义接收光源部分和阴影部分的基础色。这对于每个建筑物都是常量。
[javascript] view
plain copy
var light = new THREE.Color( 0xffffff )
var shadow = new THREE.Color( 0x303050 )
接下来我们加一些随机值作为每个建筑物的变化色。
[javascript] view
plain copy
var value = 1 - Math.random() * Math.random();
var baseColor = new THREE.Color().setRGB( value + Math.random() * 0.1, value, value + Math.random() * 0.1 );
现在我们需要给每个面的每个顶点分配.vertexColor。顶部面给baseColor,旁边的面给baseColor乘以顶部顶点的light和底部顶点的shaddow。以此来做简单的环境光遮挡效果。
[javascript] view
plain copy
// set topColor/bottom vertexColors as adjustement of baseColor
var topColor = baseColor.clone().multiply( light );
var bottomColor = baseColor.clone().multiply( shadow );
// set .vertexColors for each face
var geometry = buildingMesh.geometry;
for ( var j = 0, jl = geometry.faces.length; j < jl; j ++ ) {
if ( j === 2 ) {
// set face.vertexColors on root face
geometry.faces[ j ].vertexColors = [ baseColor, baseColor, baseColor, baseColor ];
} else {
// set face.vertexColors on sides faces
geometry.faces[ j ].vertexColors = [ topColor, bottomColor, bottomColor, topColor ];
}
}
现在单独的建筑物已经完全设置好了~!
用所有的建筑物组合成一座城市
为了制造我们的城市,我们需要整合20000个建筑物。所以我们用一个循环并且把循环中的建筑物都做以上处理。因为现在所有的建筑物都使用同样的材质,所以我们准备将它们整合到一个几何形状里。
[javascript] view
plain copy
var cityGeometry= new THREE.Geometry();
for( var i = 0; i < 20000; i ++ ){
// set the position/rotation/color the building in the city
// ...
// merge it with cityGeometry - very important for performance
THREE.GeometryUtils.merge( cityGeometry, buildingMesh );
}
现在我们得到了城市的一整个几何形体,然后为这个大几何形体创建一个网格。
[javascript] view
plain copy
// build the mesh
var material = new THREE.MeshLambertMaterial({
map : texture,
vertexColors : THREE.VertexColors
});
var mesh = new THREE.Mesh(cityGeometry, material );
这个网格就是整座城市的模型。太棒啦~!接下来是最后一步,我们会讲解如何制作贴图。
建筑物贴图的渐进生成(procedural generation)
这里我们想要生成每个建筑物侧面的纹理。简单的说,这会展示出楼层的真实感和多样性。所以它在窗户行和楼层行之间交替进行。窗户行是带着微小噪音的黑色来模拟每间房间的光线变化。然后我们小心地将纹理升级以避免滤波。
首先创建一个小的canvas画布。
[javascript] view
plain copy
var canvas = document.createElement( 'canvas' );
canvas.width = 32;
canvas.height = 64;
var context = canvas.getContext( '2d' );
然后染成白色。
[javascript] view
plain copy
context.fillStyle = '#ffffff';
context.fillRect( 0, 0, 32, 64 );
现在我们开始在这个白色的表面。我们准备在上面绘制地板。一个窗户行,一个地板行然后进行循环。实际上,当表面已经是白色的时候,我们只需要绘制窗户行就可以了。为了绘制窗户行,我们要加一些随机值以模拟窗户中的光线变化。
[javascript] view
plain copy
for( var y = 2; y < 64; y += 2 ){
for( var x = 0; x < 32; x += 2 ){
var value = Math.floor( Math.random() * 64 );
context.fillStyle = 'rgb(' + [value, value, value].join( ',' ) + ')';
context.fillRect( x, y, 2, 1 );
}
}
现在我们得到的纹理很小,为了放大后不模糊,我们关闭了.imageSmoothedEnabled效果。下面是代码:首先创建一个1024*512的大画布。
[javascript] view
plain copy
var canvas2 = document.createElement( 'canvas' );
canvas2.width = 512;
canvas2.height = 1024;
var context = canvas2.getContext( '2d' );
然后关闭平滑。
[javascript] view
plain copy
context.imageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;
现在把小的画布拷贝到大的里面。
[javascript] view
plain copy
context.drawImage( canvas, 0, 0, canvas2.width, canvas2.height );
然后我们需要做的就是创建THREE.Texture。将anisotropie设置成一个较大的值以得到更好的效果。
[javascript] view
plain copy
var texture = new THREE.Texture( generateTexture() );
texture.anisotropy = renderer.getMaxAnisotropy();
texture.needsUpdate = true;
完整代码
[javascript] view
plain copy
// build the base geometry for each building
var geometry = new THREE.CubeGeometry( 1, 1, 1 );
// translate the geometry to place the pivot point at the bottom instead of the center
geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.5, 0 ) );
// get rid of the bottom face - it is never seen
geometry.faces.splice( 3, 1 );
geometry.faceVertexUvs[0].splice( 3, 1 );
// change UVs for the top face
// - it is the roof so it wont use the same texture as the side of the building
// - set the UVs to the single coordinate 0,0. so the roof will be the same color
// as a floor row.
geometry.faceVertexUvs[0][2][0].set( 0, 0 );
geometry.faceVertexUvs[0][2][1].set( 0, 0 );
geometry.faceVertexUvs[0][2][2].set( 0, 0 );
geometry.faceVertexUvs[0][2][3].set( 0, 0 );
// buildMesh
var buildingMesh= new THREE.Mesh( geometry );
// base colors for vertexColors. light is for vertices at the top, shaddow is for the ones at the bottom
var light = new THREE.Color( 0xffffff )
var shadow = new THREE.Color( 0x303050 )
var cityGeometry= new THREE.Geometry();
for( var i = 0; i < 20000; i ++ ){
// put a random position
buildingMesh.position.x = Math.floor( Math.random() * 200 - 100 ) * 10;
buildingMesh.position.z = Math.floor( Math.random() * 200 - 100 ) * 10;
// put a random rotation
buildingMesh.rotation.y = Math.random()*Math.PI*2;
// put a random scale
buildingMesh.scale.x = Math.random() * Math.random() * Math.random() * Math.random() * 50 + 10;
buildingMesh.scale.y = (Math.random() * Math.random() * Math.random() * buildingMesh.scale.x) * 8 + 8;
buildingMesh.scale.z = buildingMesh.scale.x
// establish the base color for the buildingMesh
var value = 1 - Math.random() * Math.random();
var baseColor = new THREE.Color().setRGB( value + Math.random() * 0.1, value, value + Math.random() * 0.1 );
// set topColor/bottom vertexColors as adjustement of baseColor
var topColor = baseColor.clone().multiply( light );
var bottomColor = baseColor.clone().multiply( shadow );
// set .vertexColors for each face
var geometry = buildingMesh.geometry;
for ( var j = 0, jl = geometry.faces.length; j < jl; j ++ ) {
if ( j === 2 ) {
// set face.vertexColors on root face
geometry.faces[ j ].vertexColors = [ baseColor, baseColor, baseColor, baseColor ];
} else {
// set face.vertexColors on sides faces
geometry.faces[ j ].vertexColors = [ topColor, bottomColor, bottomColor, topColor ];
}
}
// merge it with cityGeometry - very important for performance
THREE.GeometryUtils.merge( cityGeometry, buildingMesh );
}
// generate the texture
var texture = new THREE.Texture( generateTexture() );
texture.anisotropy = renderer.getMaxAnisotropy();
texture.needsUpdate = true;
// build the mesh
var material = new THREE.MeshLambertMaterial({
map : texture,
vertexColors : THREE.VertexColors
});
var cityMesh = new THREE.Mesh(cityGeometry, material );
function generateTexture() {
// build a small canvas 32x64 and paint it in white
var canvas = document.createElement( 'canvas' );
canvas.width = 32;
canvas.height = 64;
var context = canvas.getContext( '2d' );
// plain it in white
context.fillStyle = '#ffffff';
context.fillRect( 0, 0, 32, 64 );
// draw the window rows - with a small noise to simulate light variations in each room
for( var y = 2; y < 64; y += 2 ){
for( var x = 0; x < 32; x += 2 ){
var value = Math.floor( Math.random() * 64 );
context.fillStyle = 'rgb(' + [value, value, value].join( ',' ) + ')';
context.fillRect( x, y, 2, 1 );
}
}
// build a bigger canvas and copy the small one in it
// This is a trick to upscale the texture without filtering
var canvas2 = document.createElement( 'canvas' );
canvas2.width = 512;
canvas2.height = 1024;
var context = canvas2.getContext( '2d' );
// disable smoothing
context.imageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;
// then draw the image
context.drawImage( canvas, 0, 0, canvas2.width, canvas2.height );
// return the just built canvas2
return canvas2;
}
threex.proceduralcity扩展
这段代码被集成到一个易于复用的threex包里面:threex.proceduralcity。使用起来非常简单。
[javascript] view
plain copy
var city = new THREEx.ProceduralCity()
scene.add(city)
相关文章推荐
- Three.js - 用100行javascript代码创建一座城市
- Three.js如何实现雾化效果示例代码
- ajax 异步获取数据实现代码 (js创建ajax对象)
- JS+XML 省份和城市之间的联动实现代码
- Three.js / DOC (一) 创建一个场景
- three.js第二篇【场景创建】
- 拾取模型的原理及其在THREE.JS中的代码实现
- three.js 创建一个立方体
- three.js加载obj模型的实例代码
- JavaScript的执行原理,很多人都理解错了:在js被加载后,其实就将js代码执行了一遍,在内存中创建了所有js文件中的变量。而不是激发了某个js方法后,再去相应的js文件中去执行,是去内存中执行
- asp.net通过js实现Cookie创建以及清除Cookie数组的代码
- [译] THREE.JS入门教程-6.创建自己的全景图
- 【three.js练习程序】创建地球贴图
- three.js学习点滴(1) — 创建场景
- 创建一个复制UBB软件信息的链接或按钮的js代码
- THREE.JS入门教程(4)创建粒子系统
- THREE.JS入门教程(6)创建自己的全景图实现步骤
- js实现全国省份城市级联下拉菜单效果代码
- js动态创建、删除表格示例代码