《Learn Three.js》学习(4) 材质
前言:
材质为scene中物体的皮肤,有着不同的特性和视觉效果。
材质的共有属性:
基础属性:
融合属性:
融合决定了我们渲染的颜色如何与它们后面的颜色交互
高级属性:
与WebGL内部有关
简单材质:
MeshBasicMaterial
创建材质:可以使用构造函数一次性传完所有参数;或先创建一个实例,再对实例的属性数据赋值
var meshMaterial = new THREE.MeshBasicMaterial({color: 0x7777ff});
MeshDepthMaterial
不是由光照或者某个材质决定的,而是由相机距离决定的。不需要参数
wireframe 是否显示线框
wireframe 指定线框线宽度(该属性只对THRER.CanvasRender有效)
(方块再near和far之间的距离渲染)
scene.overrideMaterial = new THREE.MeshLambertMaterial();
使用scene.overrideMaterial属性,保证场景中所有物体都使用该材质,而无需再每个THREE.Mesh对象上显示声明。
联合材质
// 创建一个深度材质,这种材质可以用来渲染对象的深度信息,通常用于特效或者后期处理。
var cubeMaterial = new THREE.MeshDepthMaterial();
// 创建一个基本颜色材质,设置颜色为绿色(0x00ff00),并且使其透明,透明度为0.6。
// blending 设置为 THREE.MultiplyBlending,这是一种混合模式,可以让颜色之间进行乘法混合,常用于创建透明或者半透明效果。
var colorMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00, // 绿色
transparent: true, // 设置材质为透明
opacity: 0.6, // 设置透明度为0.6
blending: THREE.MultiplyBlending // 设置混合模式
});
// 使用 SceneUtils 的 createMultiMaterialObject 方法创建一个多材质对象。
// 这个方法接受两个参数:几何体(cubeGeometry)和材质数组([cubeMaterial, colorMaterial])。
// 这样,同一个几何体可以被多个材质覆盖,这里是一个深度材质和一个基本颜色材质。
var cube = new THREE.SceneUtils.createMultiMaterialObject(cubeGeometry, [cubeMaterial, colorMaterial]);
// 调整第二个材质(颜色材质)的缩放比例,使其稍微小于1,这样可以创建一种视觉效果,
// 使得颜色材质看起来像是覆盖在深度材质之上的一层半透明膜。
cube.children[1].scale.set(0.99, 0.99, 0.99);
// 将创建的多材质立方体添加到场景中。
scene.add(cube);
THREE.MultiplyBlending会将前景色和背景色相乘。
MeshNormalMaterial
每一个免得颜色是由从该面向外指的法向量计算得到
for (var f = 0, fl = sphere.geometry.faces.length; f < fl; f++) {
var face = sphere.geometry.faces[f];
var centroid = new THREE.Vector3(0, 0, 0);
centroid.add(sphere.geometry.vertices[face.a]);
centroid.add(sphere.geometry.vertices[face.b]);
centroid.add(sphere.geometry.vertices[face.c]);
centroid.divideScalar(3);
var arrow = new THREE.ArrowHelper(
face.normal,
centroid,
2,
0x3333FF,
0.5,
0.5);
sphere.add(arrow);
}
对于每个THREE.Face3的对象来说,把构成该面的顶点相加再除以3来计算质心
单几何体上使用多种材质
// 多材质联合
var group = new THREE.Mesh();
var mats = [];
mats.push(new THREE.MeshBasicMaterial({color: 0x009e60}));
mats.push(new THREE.MeshBasicMaterial({color: 0x019e60}));
mats.push(new THREE.MeshBasicMaterial({color: 0x029e60}));
mats.push(new THREE.MeshBasicMaterial({color: 0x039e60}));
mats.push(new THREE.MeshBasicMaterial({color: 0x049e60}));
mats.push(new THREE.MeshBasicMaterial({color: 0x059e60}));
for (var i = 0; i < 6; i++) {
for (var j = 0; j < 6; j++) {
for (var k = 0; k < 6; k++) {
var cubeGeom = new THREE.BoxGeometry(2.9, 2.9, 2.9);
var cube = new THREE.Mesh(cubeGeom, mats);
cube.position.set(i * 3 - 3, j * 3 - 3, k * 3 - 3);
group.add(cube);
}
}
}
高级材质:
THREE.MeshLambertMaterial
可以用来创建暗淡的并不光亮的表面,材质易用,对光源有反应,支持线框绘制属性
emissive(自发光)该属性不使其成为光源,而是不受光源影响,default color Black
看起来较为暗淡 。
THREE.MeshPhongMaterial
Phong(冯氏(明暗处理算法的一种名称))
可以创建一种光亮材质,可以模拟塑料质感和金属质感
emissive的作用影响geometry的color属性
THREE.MeshStandardMaterial
更加正确的物理计算物体表面和光源的互动,更好表现塑料和金属材质
THREE.MeshPhysicalMaterial
提供更多对反光度的控制
THREE.ShaderMaterial
可以自定义着色器,直接再webgl环境下运行,将three.js中js像素转换为屏幕上的像素,且可以明确覆盖或修改或指定Three.js中的默认值
前两个即webgl中渲染基础,vertexShader顶点着色器,fragmentShader片元着色器
<!DOCTYPE html>
<html>
<head>
<title>Example 04.08 - Shader material - http://glsl.heroku.com/</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
/* set margin to 0 and overflow to hidden, to go fullscreen */
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script id="vertex-shader" type="x-shader/x-vertex">
uniform float time;
varying vec2 vUv;
void main()
{
vec3 posChanged = position;
posChanged.x = posChanged.x*(abs(sin(time*1.0)));
posChanged.y = posChanged.y*(abs(cos(time*1.0)));
posChanged.z = posChanged.z*(abs(sin(time*1.0)));
//gl_Position = projectionMatrix * modelViewMatrix * vec4(position*(abs(sin(time)/2.0)+0.5),1.0);
gl_Position = projectionMatrix * modelViewMatrix * vec4(posChanged,1.0);
}
</script>
<script id="fragment-shader-1" type="x-shader/x-fragment">
precision highp float;
uniform float time;
uniform float alpha;
uniform vec2 resolution;
varying vec2 vUv;
void main2(void)
{
vec2 position = vUv;
float red = 1.0;
float green = 0.25 + sin(time) * 0.25;
float blue = 0.0;
vec3 rgb = vec3(red, green, blue);
vec4 color = vec4(rgb, alpha);
gl_FragColor = color;
}
#define PI 3.14159
#define TWO_PI (PI*2.0)
#define N 68.5
void main(void)
{
vec2 center = (gl_FragCoord.xy);
center.x=-10.12*sin(time/200.0);
center.y=-10.12*cos(time/200.0);
vec2 v = (gl_FragCoord.xy - resolution/20.0) / min(resolution.y,resolution.x) * 15.0;
v.x=v.x-10.0;
v.y=v.y-200.0;
float col = 0.0;
for(float i = 0.0; i < N; i++)
{
float a = i * (TWO_PI/N) * 61.95;
col += cos(TWO_PI*(v.y * cos(a) + v.x * sin(a) + sin(time*0.004)*100.0 ));
}
col /= 5.0;
gl_FragColor = vec4(col*1.0, -col*1.0,-col*4.0, 1.0);
}
</script>
<script id="fragment-shader-2" type="x-shader/x-fragment">
// from http://glsl.heroku.com/e#7906.0
uniform float time;
uniform vec2 resolution;
// 2013-03-30 by @hintz
#define CGFloat float
#define M_PI 3.14159265359
vec3 hsvtorgb(float h, float s, float v)
{
float c = v * s;
h = mod((h * 6.0), 6.0);
float x = c * (1.0 - abs(mod(h, 2.0) - 1.0));
vec3 color;
if (0.0 <= h && h < 1.0)
{
color = vec3(c, x, 0.0);
}
else if (1.0 <= h && h < 2.0)
{
color = vec3(x, c, 0.0);
}
else if (2.0 <= h && h < 3.0)
{
color = vec3(0.0, c, x);
}
else if (3.0 <= h && h < 4.0)
{
color = vec3(0.0, x, c);
}
else if (4.0 <= h && h < 5.0)
{
color = vec3(x, 0.0, c);
}
else if (5.0 <= h && h < 6.0)
{
color = vec3(c, 0.0, x);
}
else
{
color = vec3(0.0);
}
color += v - c;
return color;
}
void main(void)
{
vec2 position = (gl_FragCoord.xy - 0.5 * resolution) / resolution.y;
float x = position.x;
float y = position.y;
CGFloat a = atan(x, y);
CGFloat d = sqrt(x*x+y*y);
CGFloat d0 = 0.5*(sin(d-time)+1.5)*d;
CGFloat d1 = 5.0;
CGFloat u = mod(a*d1+sin(d*10.0+time), M_PI*2.0)/M_PI*0.5 - 0.5;
CGFloat v = mod(pow(d0*4.0, 0.75),1.0) - 0.5;
CGFloat dd = sqrt(u*u+v*v);
CGFloat aa = atan(u, v);
CGFloat uu = mod(aa*3.0+3.0*cos(dd*30.0-time), M_PI*2.0)/M_PI*0.5 - 0.5;
// CGFloat vv = mod(dd*4.0,1.0) - 0.5;
CGFloat d2 = sqrt(uu*uu+v*v)*1.5;
gl_FragColor = vec4( hsvtorgb(dd+time*0.5/d1, sin(dd*time), d2), 1.0 );
}
</script>
<script id="fragment-shader-3" type="x-shader/x-fragment">
uniform vec2 resolution;
uniform float time;
vec2 rand(vec2 pos)
{
return fract( 0.00005 * (pow(pos+2.0, pos.yx + 1.0) * 22222.0));
}
vec2 rand2(vec2 pos)
{
return rand(rand(pos));
}
float softnoise(vec2 pos, float scale)
{
vec2 smplpos = pos * scale;
float c0 = rand2((floor(smplpos) + vec2(0.0, 0.0)) / scale).x;
float c1 = rand2((floor(smplpos) + vec2(1.0, 0.0)) / scale).x;
float c2 = rand2((floor(smplpos) + vec2(0.0, 1.0)) / scale).x;
float c3 = rand2((floor(smplpos) + vec2(1.0, 1.0)) / scale).x;
vec2 a = fract(smplpos);
return mix(
mix(c0, c1, smoothstep(0.0, 1.0, a.x)),
mix(c2, c3, smoothstep(0.0, 1.0, a.x)),
smoothstep(0.0, 1.0, a.y));
}
void main(void)
{
vec2 pos = gl_FragCoord.xy / resolution.y;
pos.x += time * 0.1;
float color = 0.0;
float s = 1.0;
for(int i = 0; i < 8; i++)
{
color += softnoise(pos+vec2(i)*0.02, s * 4.0) / s / 2.0;
s *= 2.0;
}
gl_FragColor = vec4(color);
}
</script>
<script id="fragment-shader-4" type="x-shader/x-fragment">
uniform float time;
uniform vec2 resolution;
vec2 rand(vec2 pos)
{
return
fract(
(
pow(
pos+2.0,
pos.yx+2.0
)*555555.0
)
);
}
vec2 rand2(vec2 pos)
{
return rand(rand(pos));
}
float softnoise(vec2 pos, float scale) {
vec2 smplpos = pos * scale;
float c0 = rand2((floor(smplpos) + vec2(0.0, 0.0)) / scale).x;
float c1 = rand2((floor(smplpos) + vec2(1.0, 0.0)) / scale).x;
float c2 = rand2((floor(smplpos) + vec2(0.0, 1.0)) / scale).x;
float c3 = rand2((floor(smplpos) + vec2(1.0, 1.0)) / scale).x;
vec2 a = fract(smplpos);
return mix(mix(c0, c1, smoothstep(0.0, 1.0, a.x)),
mix(c2, c3, smoothstep(0.0, 1.0, a.x)),
smoothstep(0.0, 1.0, a.x));
}
void main( void ) {
vec2 pos = gl_FragCoord.xy / resolution.y - time * 0.4;
float color = 0.0;
float s = 1.0;
for (int i = 0; i < 6; ++i) {
color += softnoise(pos + vec2(0.01 * float(i)), s * 4.0) / s / 2.0;
s *= 2.0;
}
gl_FragColor = vec4(color,mix(color,cos(color),sin(color)),color,1);
}
</script>
<script id="fragment-shader-5" type="x-shader/x-fragment">
uniform float time;
uniform vec2 resolution;
// tie nd die by Snoep Games.
void main( void ) {
vec3 color = vec3(1.0, 0., 0.);
vec2 pos = (( 1.4 * gl_FragCoord.xy - resolution.xy) / resolution.xx)*1.5;
float r=sqrt(pos.x*pos.x+pos.y*pos.y)/15.0;
float size1=2.0*cos(time/60.0);
float size2=2.5*sin(time/12.1);
float rot1=13.00; //82.0+16.0*sin(time/4.0);
float rot2=-50.00; //82.0+16.0*sin(time/8.0);
float t=sin(time);
float a = (60.0)*sin(rot1*atan(pos.x-size1*pos.y/r,pos.y+size1*pos.x/r)+time);
a += 200.0*acos(pos.x*2.0+cos(time/2.0))+asin(pos.y*5.0+sin(time/2.0));
a=a*(r/50.0);
a=200.0*sin(a*5.0)*(r/30.0);
if(a>5.0) a=a/200.0;
if(a<0.5) a=a*22.5;
gl_FragColor = vec4( cos(a/20.0),a*cos(a/200.0),sin(a/8.0), 1.0 );
}
</script>
<script id="fragment-shader-6" type="x-shader/x-fragment">
uniform float time;
uniform vec2 resolution;
void main( void )
{
vec2 uPos = ( gl_FragCoord.xy / resolution.xy );//normalize wrt y axis
//suPos -= vec2((resolution.x/resolution.y)/2.0, 0.0);//shift origin to center
uPos.x -= 1.0;
uPos.y -= 0.5;
vec3 color = vec3(0.0);
float vertColor = 2.0;
for( float i = 0.0; i < 15.0; ++i )
{
float t = time * (0.9);
uPos.y += sin( uPos.x*i + t+i/2.0 ) * 0.1;
float fTemp = abs(1.0 / uPos.y / 100.0);
vertColor += fTemp;
color += vec3( fTemp*(10.0-i)/10.0, fTemp*i/10.0, pow(fTemp,1.5)*1.5 );
}
vec4 color_final = vec4(color, 1.0);
gl_FragColor = color_final;
}
</script>
<div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>
<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">
// once everything is loaded, we run our Three.js stuff.
function init() {
var stats = initStats();
// create a scene, that will hold all our elements such as objects, cameras and lights.
var scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0x000000, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);
var meshMaterial1 = createMaterial("vertex-shader", "fragment-shader-1");
var meshMaterial2 = createMaterial("vertex-shader", "fragment-shader-2");
var meshMaterial3 = createMaterial("vertex-shader", "fragment-shader-3");
var meshMaterial4 = createMaterial("vertex-shader", "fragment-shader-4");
var meshMaterial5 = createMaterial("vertex-shader", "fragment-shader-5");
var meshMaterial6 = createMaterial("vertex-shader", "fragment-shader-6");
var material = new THREE.MeshFaceMaterial(
[meshMaterial1,
meshMaterial2,
meshMaterial3,
meshMaterial4,
meshMaterial5,
meshMaterial6]);
// var material = new THREE.MeshFaceMaterial([meshMaterial2, meshMaterial2, meshMaterial1, meshMaterial1, meshMaterial1, meshMaterial1]);
var cube = new THREE.Mesh(cubeGeometry, material);
// add the sphere to the scene
scene.add(cube);
// position and point the camera to the center of the scene
camera.position.x = 30;
camera.position.y = 30;
camera.position.z = 30;
camera.lookAt(new THREE.Vector3(0, 0, 0));
// add subtle ambient lighting
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// call the render function
var step = 0;
var oldContext = null;
var controls = new function () {
this.rotationSpeed = 0.02;
this.bouncingSpeed = 0.03;
this.opacity = meshMaterial1.opacity;
this.transparent = meshMaterial1.transparent;
this.visible = meshMaterial1.visible;
this.side = "front";
this.wireframe = meshMaterial1.wireframe;
this.wireframeLinewidth = meshMaterial1.wireframeLinewidth;
this.selectedMesh = "cube";
this.shadow = "flat";
};
render();
function render() {
stats.update();
cube.rotation.y = step += 0.01;
cube.rotation.x = step;
cube.rotation.z = step;
cube.material.materials.forEach(function (e) {
e.uniforms.time.value += 0.01;
});
// render using requestAnimationFrame
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
function createMaterial(vertexShader, fragmentShader) {
var vertShader = document.getElementById(vertexShader).innerHTML;
var fragShader = document.getElementById(fragmentShader).innerHTML;
var attributes = {};
var uniforms = {
time: {type: 'f', value: 0.2},
scale: {type: 'f', value: 0.2},
alpha: {type: 'f', value: 0.6},
resolution: {type: "v2", value: new THREE.Vector2()}
};
uniforms.resolution.value.x = window.innerWidth;
uniforms.resolution.value.y = window.innerHeight;
var meshMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
attributes: attributes,
vertexShader: vertShader,
fragmentShader: fragShader,
transparent: true
});
return meshMaterial;
}
}
window.onload = init;
</script>
</body>
</html>
线性几何体材质:
THREE.LineBasicMaterial
// 调用gosper函数生成一个4阶,长度为60的Gosper曲线的点集
var points = gosper(4, 60);
// 创建一个新的THREE.BufferGeometry对象,用于存储线条段的几何数据
var lineWidth = new THREE.BufferGeometry();
// 创建一个空数组来存储颜色值
var colors = [];
// 初始化一个计数器变量i,用于跟踪当前处理的点的索引
var i = 0;
// 遍历points数组中的每个点
points.forEach(function(e){
// 将每个点的坐标添加到LineSegments的顶点数组中
LineSegments.vertices.push(new THREE.Vector3(e.x, e.y, e.z));
// 为每个点设置一个颜色值,这里使用白色作为基础颜色
colors[i] = new THREE.Color(0xffffff);
// 根据点的x和y坐标计算HSL颜色值,并设置颜色
// x坐标除以100后加上0.5作为色相,y坐标乘以20后除以300再加上0.5作为饱和度,亮度固定为0.8
colors[i].setHSL(e.x / 100 + 0.5, (e.y * 20) / 300 + 0.5, 0.8);
// 增加计数器i,以便为下一个点设置颜色
i++;
});
// 定义一个基本的线条材质,其中包含不透明度、线宽和顶点颜色的设置
var material = new THREE.LineBasicMaterial({
opacity: 1.0, // 设置线条的不透明度为1.0(完全不透明)
linewidth: 1, // 设置线条的宽度为1
vertexColors: THREE.VertexColors // 启用顶点颜色,这意味着每个顶点可以有自己的颜色
});
// 将计算出的颜色数组赋值给LineSegments对象的colors属性
LineSegments.colors = colors;
THREE.LineDashMaterial
在LineBasicMaterial的基础上有:scale(缩放dashSize和gapSize,scale < 1 dashSize和gapSize增大);dashSize 虚线端的长度; gapSize虚线间隔的宽度
const material = new THREE.LineDashedMaterial( {
color: 0xffffff,
linewidth: 1,
scale: 1,
dashSize: 3,
gapSize: 1,
} );