Three 之 three.js (webgl)涉及的各种材质简单说明(常用材质配有效果图)
Three 之 three.js (webgl)涉及的各种材质简单说明(常用材质有效果图)
目录
Three 之 three.js (webgl)涉及的各种材质简单说明(常用材质配有效果图)
4、MeshLambertMaterial Lambert网格材质
一、简单介绍
Three js 开发的一些知识整理,方便后期遇到类似的问题,能够及时查阅使用。
本节介绍, three.js (webgl)中有各种材质,不同材质又有不同的效果,这里做简单的介绍,如果有不足之处,欢迎指出,或者你有更好的方法,欢迎留言。
Materail 材料的抽象基类。
材料描述物体的外观。它们是以一种(主要)与渲染无关的方式定义的,所以如果你决定使用不同的渲染器,你不必重写材料。
以下属性和方法可以被所有其他材质类型继承(尽管它们可能有不同的默认值)。
属性 :
.alphaTest:Float
设置要在运行alpha测试时使用的alpha值。如果不透明度低于这个值,材质将不会被渲染。默认值为0。
.alphaToCoverage:Float
使alpha覆盖范围。只能在启用msaa的上下文中使用(意味着在创建渲染器时将antialias参数设置为true)。默认是假的。
.blendDst:Integer
混合的目的地。默认是OneMinusSrcAlphaFactor。请参阅所有可能值的目标因子常数。
材质的混合必须设置为customblend,这样才会有效果。
.blendDstAlpha:Integer
. blenddst的透明度。如果为空,则使用. blenddst值。默认为空。
方法:
EventDispatcher方法在这个类上可用。
.clone():Material
返回一个与此材料具有相同参数的新材料。
.copy(material : material):this
将参数从传递的材料复制到这个材料中。
.dispose():undefined
这就处理了材料。材料的纹理不会被丢弃。这些需要被纹理处理。
.onBeforeCompile(shader : Shader, renderer : WebGLRenderer ):undefined
一个可选的回调函数,在着色器程序编译之前立即执行。这个函数被shader源代码作为参数调用。用于修改内置材质。
等等,很多属性方法,这里不再一一介绍
可到官网查看:three.js docs
相关注意说明:以下涉及代码都基于 Threejs GitHub - mrdoob/three.js: JavaScript 3D Library.
二、各种材质说明
1、MeshBasicMaterial 基础网格材质
一个以简单着色(平面或线框)方式来绘制几何体的材质。这种材质不受光照的影响。
MeshBasicMaterial( parameters : Object )
parameters - (可选)用于定义材质外观的对象,具有一个或多个属性。材质的任何属性都可以从此处传入(包括从Material继承的任何属性)。
属性color例外,其可以作为十六进制字符串传递,默认情况下为 0xffffff(白色),内部调用Color.set(color)。
2、MeshDepthMaterial 深度网格材质
一种按深度绘制几何体的材质。深度基于相机远近平面。白色最近,黑色最远。
3、MeshDistanceMaterial 距离网格材质
MeshDistanceMaterial 在内部用于使用PointLight来实现阴影映射。 也可以用于通过将MeshDistanceMaterial实例指定给Object3D.customDistanceMaterial,来自定义物体阴影投射。
下列示例演示了这一方法,以确保物体的透明部分不投射阴影。
4、MeshLambertMaterial Lambert网格材质
一种非光泽表面的材质,没有镜面高光。
该材质使用基于非物理的Lambertian模型来计算反射率。 这可以很好地模拟一些表面(例如未经处理的木材或石材),但不能模拟具有镜面高光的光泽表面(例如涂漆木材)。
使用Gouraud着色模型计算着色。这将计算每个顶点的着色 (即在vertex shader中)并在多边形的面上插入结果。
由于反射率和光照模型的简单性,MeshPhongMaterial,MeshStandardMaterial或者MeshPhysicalMaterial 上使用这种材质时会以一些图形精度为代价,得到更高的性能。
5、MeshMatcapMaterial
MeshMatcapMaterial 由一个材质捕捉(MatCap,或光照球(Lit Sphere))纹理所定义,其编码了材质的颜色与明暗。
由于mapcap图像文件编码了烘焙过的光照,因此MeshMatcapMaterial 不对灯光作出反应。 它将会投射阴影到一个接受阴影的物体上(and shadow clipping works),但不会产生自身阴影或是接受阴影。
6、MeshNormalMaterial 法线网格材质
一种把法向量映射到RGB颜色的材质。
7、MeshPhongMaterial Phong网格材质
一种用于具有镜面高光的光泽表面的材质。
该材质使用非物理的Blinn-Phong模型来计算反射率。 与MeshLambertMaterial中使用的Lambertian模型不同,该材质可以模拟具有镜面高光的光泽表面(例如涂漆木材)。
使用Phong着色模型计算着色时,会计算每个像素的阴影(在fragment shader, AKA pixel shader中),与MeshLambertMaterial使用的Gouraud模型相比,该模型的结果更准确,但代价是牺牲一些性能。 MeshStandardMaterial和MeshPhysicalMaterial也使用这个着色模型。
在MeshStandardMaterial或MeshPhysicalMaterial上使用此材质时,性能通常会更高 ,但会牺牲一些图形精度。
8、MeshPhysicalMaterial 物理网格材质
MeshStandardMaterial的扩展,提供了更高级的基于物理的渲染属性:
- Clearcoat: 有些类似于车漆,碳纤,被水打湿的表面的材质需要在面上再增加一个透明的,具有一定反光特性的面。而且这个面说不定有一定的起伏与粗糙度。Clearcoat可以在不需要重新创建一个透明的面的情况下做到类似的效果。
- 基于物理的透明度:.opacity属性有一些限制:在透明度比较高的时候,反射也随之减少。使用基于物理的透光性.transmission属性可以让一些很薄的透明表面,例如玻璃,变得更真实一些。
- 高级光线反射: 为非金属材质提供了更多更灵活的光线反射。
物理网格材质使用了更复杂的着色器功能,所以在每个像素的渲染都要比three.js中的其他材质更费性能,大部分的特性是默认关闭的,需要手动开启,每开启一项功能在开启的时候才会更耗性能。请注意,为获得最佳效果,您在使用此材质时应始终指定environment map。
9、MeshStandardMaterial 标准网格材质
一种基于物理的标准材质,使用Metallic-Roughness工作流程。
基于物理的渲染(PBR)最近已成为许多3D应用程序的标准,例如Unity, Unreal和 3D Studio Max。
这种方法与旧方法的不同之处在于,不使用近似值来表示光与表面的相互作用,而是使用物理上正确的模型。 我们的想法是,不是在特定照明下调整材质以使其看起来很好,而是可以创建一种材质,能够“正确”地应对所有光照场景。
在实践中,该材质提供了比MeshLambertMaterial 或MeshPhongMaterial 更精确和逼真的结果,代价是计算成本更高。
计算着色的方式与MeshPhongMaterial相同,都使用Phong着色模型, 这会计算每个像素的阴影(即在fragment shader, AKA pixel shader中), 与MeshLambertMaterial使用的Gouraud模型相比,该模型的结果更准确,但代价是牺牲一些性能。
请注意,为获得最佳效果,您在使用此材质时应始终指定environment map。
有关PBR概念的非技术性介绍以及如何设置PBR材质,请查看marmoset成员的这些文章:
在 three.js(以及其他大多数PBR系统)中使用方法的技术细节, 可以在Brent Burley撰写的paper from Disney (pdf) 中查看。
10、MeshToonMaterial 卡通网格材质
A material implementing toon shading.
11、PointsMaterial 点材质
Points使用的默认材质。
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - buffergeometry - particles</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="container"></div>
<div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - buffergeometry - particles</div>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from './jsm/libs/stats.module.js';
let container, stats;
let camera, scene, renderer;
let points;
init();
animate();
function init() {
container = document.getElementById( 'container' );
//
camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 5, 3500 );
camera.position.z = 2750;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x050505 );
scene.fog = new THREE.Fog( 0x050505, 2000, 3500 );
//
const particles = 500000;
const geometry = new THREE.BufferGeometry();
const positions = [];
const colors = [];
const color = new THREE.Color();
const n = 1000, n2 = n / 2; // particles spread in the cube
for ( let i = 0; i < particles; i ++ ) {
// positions
const x = Math.random() * n - n2;
const y = Math.random() * n - n2;
const z = Math.random() * n - n2;
positions.push( x, y, z );
// colors
const vx = ( x / n ) + 0.5;
const vy = ( y / n ) + 0.5;
const vz = ( z / n ) + 0.5;
color.setRGB( vx, vy, vz );
colors.push( color.r, color.g, color.b );
}
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
geometry.computeBoundingSphere();
//
const material = new THREE.PointsMaterial( { size: 15, vertexColors: true } );
points = new THREE.Points( geometry, material );
scene.add( points );
//
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//
stats = new Stats();
container.appendChild( stats.dom );
//
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
const time = Date.now() * 0.001;
points.rotation.x = time * 0.25;
points.rotation.y = time * 0.5;
renderer.render( scene, camera );
}
</script>
</body>
</html>
12、RawShaderMaterial 原始着色器材质
此类的工作方式与ShaderMaterial类似,不同之处在于内置的uniforms和attributes的定义不会自动添加到GLSL shader代码中。
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - raw shader</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="container"></div>
<div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - raw shader demo</div>
<script id="vertexShader" type="x-shader/x-vertex">
precision mediump float;
precision mediump int;
uniform mat4 modelViewMatrix; // optional
uniform mat4 projectionMatrix; // optional
attribute vec3 position;
attribute vec4 color;
varying vec3 vPosition;
varying vec4 vColor;
void main() {
vPosition = position;
vColor = color;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
precision mediump int;
uniform float time;
varying vec3 vPosition;
varying vec4 vColor;
void main() {
vec4 color = vec4( vColor );
color.r += sin( vPosition.x * 10.0 + time ) * 0.5;
gl_FragColor = color;
}
</script>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from './jsm/libs/stats.module.js';
let container, stats;
let camera, scene, renderer;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 10 );
camera.position.z = 2;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x101010 );
// geometry
// nr of triangles with 3 vertices per triangle
const vertexCount = 200 * 3;
const geometry = new THREE.BufferGeometry();
const positions = [];
const colors = [];
for ( let i = 0; i < vertexCount; i ++ ) {
// adding x,y,z
positions.push( Math.random() - 0.5 );
positions.push( Math.random() - 0.5 );
positions.push( Math.random() - 0.5 );
// adding r,g,b,a
colors.push( Math.random() * 255 );
colors.push( Math.random() * 255 );
colors.push( Math.random() * 255 );
colors.push( Math.random() * 255 );
}
const positionAttribute = new THREE.Float32BufferAttribute( positions, 3 );
const colorAttribute = new THREE.Uint8BufferAttribute( colors, 4 );
colorAttribute.normalized = true; // this will map the buffer values to 0.0f - +1.0f in the shader
geometry.setAttribute( 'position', positionAttribute );
geometry.setAttribute( 'color', colorAttribute );
// material
const material = new THREE.RawShaderMaterial( {
uniforms: {
time: { value: 1.0 }
},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
side: THREE.DoubleSide,
transparent: true
} );
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
stats = new Stats();
container.appendChild( stats.dom );
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
const time = performance.now();
const object = scene.children[ 0 ];
object.rotation.y = time * 0.0005;
object.material.uniforms.time.value = time * 0.005;
renderer.render( scene, camera );
}
</script>
</body>
</html>
13、ShaderMaterial 着色器材质
使用自定义shader渲染的材质。 shader是一个用GLSL编写的小程序 ,在GPU上运行。 您可能需要使用自定义shader,如果你要:
- 要实现内置 materials 之外的效果。
- 将许多对象组合成单个BufferGeometry以提高性能。
使用ShaderMaterial时需要注意以下注意事项:
- ShaderMaterial 只有使用 WebGLRenderer 才可以绘制正常, 因为 vertexShader 和 fragmentShader 属性中GLSL代码必须使用WebGL来编译并运行在GPU中。
- 从 THREE r72开始,不再支持在ShaderMaterial中直接分配属性。 必须使用 BufferGeometry实例,使用BufferAttribute实例来定义自定义属性。
- 从 THREE r77开始,WebGLRenderTarget 或 WebGLCubeRenderTarget 实例不再被用作uniforms。 必须使用它们的texture 属性。
- 内置attributes和uniforms与代码一起传递到shaders。 如果您不希望WebGLProgram向shader代码添加任何内容,则可以使用RawShaderMaterial而不是此类。
- 您可以使用指令#pragma unroll_loop_start,#pragma unroll_loop_end 以便通过shader预处理器在GLSL中展开for循环。 该指令必须放在循环的正上方。循环格式必须与定义的标准相对应。
- 循环必须标准化normalized。
- 循环变量必须是i。
- The value UNROLLED_LOOP_INDEX will be replaced with the explicity value of i for the given iteration and can be used in preprocessor statements.
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - buffergeometry - selective - draw</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
<script type="x-shader/x-vertex" id="vertexshader">
attribute float visible;
varying float vVisible;
attribute vec3 vertColor;
varying vec3 vColor;
void main() {
vColor = vertColor;
vVisible = visible;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
varying float vVisible;
varying vec3 vColor;
void main() {
if ( vVisible > 0.0 ) {
gl_FragColor = vec4( vColor, 1.0 );
} else {
discard;
}
}
</script>
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> buffergeometry - selective - draw
<div id="title"></div>
<div id="ui"><a href="#" id="hideLines">CULL SOME LINES</a> - <a href="#" id="showAllLines">SHOW ALL LINES</a></div>
</div>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from './jsm/libs/stats.module.js';
let camera, scene, renderer, stats;
let geometry, mesh;
const numLat = 100;
const numLng = 200;
let numLinesCulled = 0;
init();
animate();
function init() {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 3.5;
scene.add( new THREE.AmbientLight( 0x444444 ) );
stats = new Stats();
document.body.appendChild( stats.dom );
window.addEventListener( 'resize', onWindowResize );
addLines( 1.0 );
const hideLinesButton = document.getElementById( 'hideLines' );
hideLinesButton.addEventListener( 'click', hideLines );
const showAllLinesButton = document.getElementById( 'showAllLines' );
showAllLinesButton.addEventListener( 'click', showAllLines );
}
function addLines( radius ) {
geometry = new THREE.BufferGeometry();
const linePositions = new Float32Array( numLat * numLng * 3 * 2 );
const lineColors = new Float32Array( numLat * numLng * 3 * 2 );
const visible = new Float32Array( numLat * numLng * 2 );
for ( let i = 0; i < numLat; ++ i ) {
for ( let j = 0; j < numLng; ++ j ) {
const lat = ( Math.random() * Math.PI ) / 50.0 + i / numLat * Math.PI;
const lng = ( Math.random() * Math.PI ) / 50.0 + j / numLng * 2 * Math.PI;
const index = i * numLng + j;
linePositions[ index * 6 + 0 ] = 0;
linePositions[ index * 6 + 1 ] = 0;
linePositions[ index * 6 + 2 ] = 0;
linePositions[ index * 6 + 3 ] = radius * Math.sin( lat ) * Math.cos( lng );
linePositions[ index * 6 + 4 ] = radius * Math.cos( lat );
linePositions[ index * 6 + 5 ] = radius * Math.sin( lat ) * Math.sin( lng );
const color = new THREE.Color( 0xffffff );
color.setHSL( lat / Math.PI, 1.0, 0.2 );
lineColors[ index * 6 + 0 ] = color.r;
lineColors[ index * 6 + 1 ] = color.g;
lineColors[ index * 6 + 2 ] = color.b;
color.setHSL( lat / Math.PI, 1.0, 0.7 );
lineColors[ index * 6 + 3 ] = color.r;
lineColors[ index * 6 + 4 ] = color.g;
lineColors[ index * 6 + 5 ] = color.b;
// non-0 is visible
visible[ index * 2 + 0 ] = 1.0;
visible[ index * 2 + 1 ] = 1.0;
}
}
geometry.setAttribute( 'position', new THREE.BufferAttribute( linePositions, 3 ) );
geometry.setAttribute( 'vertColor', new THREE.BufferAttribute( lineColors, 3 ) );
geometry.setAttribute( 'visible', new THREE.BufferAttribute( visible, 1 ) );
geometry.computeBoundingSphere();
const shaderMaterial = new THREE.ShaderMaterial( {
vertexShader: document.getElementById( 'vertexshader' ).textContent,
fragmentShader: document.getElementById( 'fragmentshader' ).textContent
} );
mesh = new THREE.LineSegments( geometry, shaderMaterial );
scene.add( mesh );
updateCount();
}
function updateCount() {
const str = '1 draw call, ' + numLat * numLng + ' lines, ' + numLinesCulled + ' culled (<a target="_blank" href="http://callum.com">author</a>)';
document.getElementById( 'title' ).innerHTML = str.replace( /\B(?=(\d{3})+(?!\d))/g, ',' );
}
function hideLines() {
for ( let i = 0; i < geometry.attributes.visible.array.length; i += 2 ) {
if ( Math.random() > 0.75 ) {
if ( geometry.attributes.visible.array[ i + 0 ] ) {
++ numLinesCulled;
}
geometry.attributes.visible.array[ i + 0 ] = 0;
geometry.attributes.visible.array[ i + 1 ] = 0;
}
}
geometry.attributes.visible.needsUpdate = true;
updateCount();
}
function showAllLines() {
numLinesCulled = 0;
for ( let i = 0; i < geometry.attributes.visible.array.length; i += 2 ) {
geometry.attributes.visible.array[ i + 0 ] = 1;
geometry.attributes.visible.array[ i + 1 ] = 1;
}
geometry.attributes.visible.needsUpdate = true;
updateCount();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
const time = Date.now() * 0.001;
mesh.rotation.x = time * 0.25;
mesh.rotation.y = time * 0.5;
stats.update();
renderer.render( scene, camera );
}
</script>
</body>
</html>
14、ShadowMaterial 阴影材质
此材质可以接收阴影,但在其他方面完全透明。
15、SpriteMaterial 点精灵材质
一种使用Sprite的材质。
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - sprites</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - sprites
</div>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
let camera, scene, renderer;
let cameraOrtho, sceneOrtho;
let spriteTL, spriteTR, spriteBL, spriteBR, spriteC;
let mapC;
let group;
init();
animate();
function init() {
const width = window.innerWidth;
const height = window.innerHeight;
camera = new THREE.PerspectiveCamera( 60, width / height, 1, 2100 );
camera.position.z = 1500;
cameraOrtho = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, 1, 10 );
cameraOrtho.position.z = 10;
scene = new THREE.Scene();
scene.fog = new THREE.Fog( 0x000000, 1500, 2100 );
sceneOrtho = new THREE.Scene();
// create sprites
const amount = 200;
const radius = 500;
const textureLoader = new THREE.TextureLoader();
textureLoader.load( 'textures/sprite0.png', createHUDSprites );
const mapB = textureLoader.load( 'textures/sprite1.png' );
mapC = textureLoader.load( 'textures/sprite2.png' );
group = new THREE.Group();
const materialC = new THREE.SpriteMaterial( { map: mapC, color: 0xffffff, fog: true } );
const materialB = new THREE.SpriteMaterial( { map: mapB, color: 0xffffff, fog: true } );
for ( let a = 0; a < amount; a ++ ) {
const x = Math.random() - 0.5;
const y = Math.random() - 0.5;
const z = Math.random() - 0.5;
let material;
if ( z < 0 ) {
material = materialB.clone();
} else {
material = materialC.clone();
material.color.setHSL( 0.5 * Math.random(), 0.75, 0.5 );
material.map.offset.set( - 0.5, - 0.5 );
material.map.repeat.set( 2, 2 );
}
const sprite = new THREE.Sprite( material );
sprite.position.set( x, y, z );
sprite.position.normalize();
sprite.position.multiplyScalar( radius );
group.add( sprite );
}
scene.add( group );
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.autoClear = false; // To allow render overlay on top of sprited sphere
document.body.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize );
}
function createHUDSprites( texture ) {
const material = new THREE.SpriteMaterial( { map: texture } );
const width = material.map.image.width;
const height = material.map.image.height;
spriteTL = new THREE.Sprite( material );
spriteTL.center.set( 0.0, 1.0 );
spriteTL.scale.set( width, height, 1 );
sceneOrtho.add( spriteTL );
spriteTR = new THREE.Sprite( material );
spriteTR.center.set( 1.0, 1.0 );
spriteTR.scale.set( width, height, 1 );
sceneOrtho.add( spriteTR );
spriteBL = new THREE.Sprite( material );
spriteBL.center.set( 0.0, 0.0 );
spriteBL.scale.set( width, height, 1 );
sceneOrtho.add( spriteBL );
spriteBR = new THREE.Sprite( material );
spriteBR.center.set( 1.0, 0.0 );
spriteBR.scale.set( width, height, 1 );
sceneOrtho.add( spriteBR );
spriteC = new THREE.Sprite( material );
spriteC.center.set( 0.5, 0.5 );
spriteC.scale.set( width, height, 1 );
sceneOrtho.add( spriteC );
updateHUDSprites();
}
function updateHUDSprites() {
const width = window.innerWidth / 2;
const height = window.innerHeight / 2;
spriteTL.position.set( - width, height, 1 ); // top left
spriteTR.position.set( width, height, 1 ); // top right
spriteBL.position.set( - width, - height, 1 ); // bottom left
spriteBR.position.set( width, - height, 1 ); // bottom right
spriteC.position.set( 0, 0, 1 ); // center
}
function onWindowResize() {
const width = window.innerWidth;
const height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
cameraOrtho.left = - width / 2;
cameraOrtho.right = width / 2;
cameraOrtho.top = height / 2;
cameraOrtho.bottom = - height / 2;
cameraOrtho.updateProjectionMatrix();
updateHUDSprites();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
const time = Date.now() / 1000;
for ( let i = 0, l = group.children.length; i < l; i ++ ) {
const sprite = group.children[ i ];
const material = sprite.material;
const scale = Math.sin( time + sprite.position.x * 0.01 ) * 0.3 + 1.0;
let imageWidth = 1;
let imageHeight = 1;
if ( material.map && material.map.image && material.map.image.width ) {
imageWidth = material.map.image.width;
imageHeight = material.map.image.height;
}
sprite.material.rotation += 0.1 * ( i / l );
sprite.scale.set( scale * imageWidth, scale * imageHeight, 1.0 );
if ( material.map !== mapC ) {
material.opacity = Math.sin( time + sprite.position.x * 0.01 ) * 0.4 + 0.6;
}
}
group.rotation.x = time * 0.5;
group.rotation.y = time * 0.75;
group.rotation.z = time * 1.0;
renderer.clear();
renderer.render( scene, camera );
renderer.clearDepth();
renderer.render( sceneOrtho, cameraOrtho );
}
</script>
</body>
</html>
16、LineBasicMaterial 基础线条材质
一种用于绘制线框样式几何体的材质。
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - buffergeometry - lines</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="container"></div>
<div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - buffergeometry - lines</div>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from './jsm/libs/stats.module.js';
let container, stats, clock;
let camera, scene, renderer;
let line;
const segments = 10000;
const r = 800;
let t = 0;
init();
animate();
function init() {
container = document.getElementById( 'container' );
//
camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 1, 4000 );
camera.position.z = 2750;
scene = new THREE.Scene();
clock = new THREE.Clock();
const geometry = new THREE.BufferGeometry();
const material = new THREE.LineBasicMaterial( { vertexColors: true } );
const positions = [];
const colors = [];
for ( let i = 0; i < segments; i ++ ) {
const x = Math.random() * r - r / 2;
const y = Math.random() * r - r / 2;
const z = Math.random() * r - r / 2;
// positions
positions.push( x, y, z );
// colors
colors.push( ( x / r ) + 0.5 );
colors.push( ( y / r ) + 0.5 );
colors.push( ( z / r ) + 0.5 );
}
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
generateMorphTargets( geometry );
geometry.computeBoundingSphere();
line = new THREE.Line( geometry, material );
scene.add( line );
//
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild( renderer.domElement );
//
stats = new Stats();
container.appendChild( stats.dom );
//
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
const delta = clock.getDelta();
const time = clock.getElapsedTime();
line.rotation.x = time * 0.25;
line.rotation.y = time * 0.5;
t += delta * 0.5;
line.morphTargetInfluences[ 0 ] = Math.abs( Math.sin( t ) );
renderer.render( scene, camera );
}
function generateMorphTargets( geometry ) {
const data = [];
for ( let i = 0; i < segments; i ++ ) {
const x = Math.random() * r - r / 2;
const y = Math.random() * r - r / 2;
const z = Math.random() * r - r / 2;
data.push( x, y, z );
}
const morphTarget = new THREE.Float32BufferAttribute( data, 3 );
morphTarget.name = 'target1';
geometry.morphAttributes.position = [ morphTarget ];
}
</script>
</body>
</html>
17、LineDashedMaterial 虚线材质
一种用于绘制虚线样式几何体的材质。
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - dashed lines</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - dashed lines example</div>
<div id="container"></div>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from './jsm/libs/stats.module.js';
import * as GeometryUtils from './jsm/utils/GeometryUtils.js';
let renderer, scene, camera, stats;
const objects = [];
const WIDTH = window.innerWidth, HEIGHT = window.innerHeight;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 60, WIDTH / HEIGHT, 1, 200 );
camera.position.z = 150;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x111111 );
scene.fog = new THREE.Fog( 0x111111, 150, 200 );
const subdivisions = 6;
const recursion = 1;
const points = GeometryUtils.hilbert3D( new THREE.Vector3( 0, 0, 0 ), 25.0, recursion, 0, 1, 2, 3, 4, 5, 6, 7 );
const spline = new THREE.CatmullRomCurve3( points );
const samples = spline.getPoints( points.length * subdivisions );
const geometrySpline = new THREE.BufferGeometry().setFromPoints( samples );
const line = new THREE.Line( geometrySpline, new THREE.LineDashedMaterial( { color: 0xffffff, dashSize: 1, gapSize: 0.5 } ) );
line.computeLineDistances();
objects.push( line );
scene.add( line );
const geometryBox = box( 50, 50, 50 );
const lineSegments = new THREE.LineSegments( geometryBox, new THREE.LineDashedMaterial( { color: 0xffaa00, dashSize: 3, gapSize: 1 } ) );
lineSegments.computeLineDistances();
objects.push( lineSegments );
scene.add( lineSegments );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( WIDTH, HEIGHT );
const container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
stats = new Stats();
container.appendChild( stats.dom );
//
window.addEventListener( 'resize', onWindowResize );
}
function box( width, height, depth ) {
width = width * 0.5,
height = height * 0.5,
depth = depth * 0.5;
const geometry = new THREE.BufferGeometry();
const position = [];
position.push(
- width, - height, - depth,
- width, height, - depth,
- width, height, - depth,
width, height, - depth,
width, height, - depth,
width, - height, - depth,
width, - height, - depth,
- width, - height, - depth,
- width, - height, depth,
- width, height, depth,
- width, height, depth,
width, height, depth,
width, height, depth,
width, - height, depth,
width, - height, depth,
- width, - height, depth,
- width, - height, - depth,
- width, - height, depth,
- width, height, - depth,
- width, height, depth,
width, height, - depth,
width, height, depth,
width, - height, - depth,
width, - height, depth
);
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( position, 3 ) );
return geometry;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
const time = Date.now() * 0.001;
scene.traverse( function ( object ) {
if ( object.isLine ) {
object.rotation.x = 0.25 * time;
object.rotation.y = 0.25 * time;
}
} );
renderer.render( scene, camera );
}
</script>
</body>
</html>