threejs透明贴图如何不影响内部材质

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了threejs透明贴图如何不影响内部材质相关的知识,希望对你有一定的参考价值。

在Three.js中,如果要实现不影响内部材质的透明贴图,则需要对渲染器的混合模式进行设置。具体操作如下:

1. 设置材质的混合模式。可以通过设置material.blending参数来完成。例如将blending设置为THREE.NormalBlending表示材质使用正常的混合模式。如果该参数设置为THREE.CustomBlending,则需要进一步设置material.blendSrc和material.blendDst参数来指定混合源和混合目标。

2. 通过调整渲染器的渲染顺序来避免内部材质的透明被多次覆盖。可以通过设置material.depthTest和material.depthWrite参数来实现。通常情况下,需要将这两个参数都设置为true,以确保在深度测试过程中材质的深度值得到正确的比较和保存。

3. 对于透明贴图本身,需要将其加载为一个带有alpha通道的纹理贴图,并将material.opacity属性设置为小于1的数值,以表明材质是半透明的。

综上所述,通过设置合适的混合模式、渲染顺序和透明度等参数,就可以在Three.js中实现不影响内部材质的透明贴图了。
参考技术A 在Three.js中,可以通过设置材质的混合模式(blending)来实现透明效果。默认情况下,当使用透明贴图时,内部材质也会受到影响。

为了避免这种情况发生,可以在创建材质时将transparent属性设置为true,并且将depthWrite属性设置为false。这样就能够让透明部分不影响内部材质的显示。

示例代码如下:

```javascript
var texture = new THREE.TextureLoader().load( 'texture.png' );
var material = new THREE.MeshPhongMaterial(
map: texture,
transparent: true, // 设置为透明
blending: THREE.NormalBlending, // 混合模式
depthWrite: false // 不写入深度缓存
);
```

需要注意的是,在使用该方法时需要确保场景中其他物体的渲染顺序正确。如果有多个半透明物体重叠在一起,则可能会出现不正确的渲染结果。此时可以考虑调整它们之间的层级关系或者使用更复杂的混合模式来解决问题。
参考技术B 可以使用混合函数来实现,混合函数允许您使用不同的混合模式,以及将一个图像与另一个图像进行混合。您可以使用混合函数将图层与透明度混合,以便只显示透明部分,而不影响内部材质。此外,您还可以使用色调映射来改变要显示的部分,以便只显示内部材质,而不影响外部图像。

threejs 粒子系统和材质贴图

例子系统

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - buffer geometry custom attributes - particles</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<style>
			body 
				color: #ffffff;
				background-color: #000000;
				margin: 0px;
				overflow: hidden;
			
			#info 
				position: absolute;
				top: 0px;
				width: 100%;
				padding: 5px;
				font-family: Monospace;
				font-size: 13px;
				text-align: center;
				font-weight: bold;
			
			a 
				color: #fff;
			
		</style>
	</head>
 
	<body>
		<div id="container"></div>
		<script src="../build/three.js"></script>
		<script type="x-shader/x-vertex" id="vertexshader">
 
			attribute float size;
			attribute vec3 customColor;
 
			varying vec3 vColor;
 
			void main() 
 
				vColor = customColor;
 
				vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
 
				gl_PointSize = size * ( 300.0 / mvPosition.x );
 
				gl_Position = projectionMatrix * mvPosition;
 
			
 
		</script>
 
		<script type="x-shader/x-fragment" id="fragmentshader">
 
			uniform sampler2D texture;
 
			varying vec3 vColor;
 
			void main() 
				gl_FragColor = vec4( vColor, 1.0 );
				gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
 
			
 
		</script>
 
		<script>
 
		var renderer, scene, camera, stats;
 
		var particleSystem, uniforms, geometry;
 
		var particles = 200;
 
		var WIDTH = window.innerWidth;
		var HEIGHT = window.innerHeight;
 
		init();
		animate();
 
		function init() 
 
			camera = new THREE.PerspectiveCamera( 40, WIDTH / HEIGHT, 1, 10000 );
			camera.position.z = 500;
			scene = new THREE.Scene();
			uniforms = 
				texture:    value: new THREE.TextureLoader().load( "textures/sprites/spark1.png" ) 
			;
			var shaderMaterial = new THREE.ShaderMaterial( 
 
				uniforms:       uniforms,
				vertexShader:   document.getElementById( 'vertexshader' ).textContent,
				fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
				transparent:    true
			);
 
			var radius = 400;
			var geometry = new THREE.BufferGeometry();
			var positions = new Float32Array( particles * 3 );
			var colors = new Float32Array( particles * 3 );
			var sizes = new Float32Array( particles );
			for ( var i = 0, i3 = 0; i < particles; i ++, i3 += 3 ) 
				positions[ i3 + 0 ] = ( Math.random() * 2 - 1 ) * radius;
				positions[ i3 + 1 ] = ( Math.random() * 2 - 1 ) * radius;
				positions[ i3 + 2 ] = 0;
				colors[ i3 + 0 ] = 1;
				colors[ i3 + 1 ] = 1;
				colors[ i3 + 2 ] = 1;
				sizes[ i ] = 10;
			
			geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
			geometry.addAttribute( 'customColor', new THREE.BufferAttribute( colors, 3 ) );
			geometry.addAttribute( 'size', new THREE.BufferAttribute( sizes, 1 ) );
			particleSystem = new THREE.Points( geometry, shaderMaterial );
			scene.add( particleSystem );
			renderer = new THREE.WebGLRenderer();
			renderer.setPixelRatio( window.devicePixelRatio );
			renderer.setSize( WIDTH, HEIGHT );
 
			var container = document.getElementById( 'container' );
			container.appendChild( renderer.domElement );
 
			window.addEventListener( 'resize', onWindowResize, false );
 
		
 
		function onWindowResize() 
			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();
			renderer.setSize( window.innerWidth, window.innerHeight );
		
 
		function animate() 
			requestAnimationFrame( animate );
			renderer.render( scene, camera );
 
		
 
	</script>
 
</body>
</html>

材质贴图

高光网格材质 MeshPhongMaterial、标准网格材质MeshStandardMaterial、物理网格材质MeshPhysicalMaterial,次时代、PBR

次时代和 PBR

如果你想展示一个三维场景,比如一辆轿车,首先需要 3D 美术建模和烘培,然后程序员通过 Three.js 引擎加载解析显示出来。

对于3D美术来说烘培的时候有次时代、PBR 两种流程,这两种所谓的流程,对应的就是 Three.js 的高光网格材质 MeshPhongMaterial、基于物理的材质MeshStandardMaterial或MeshPhysicalMaterial。

对于程序员而言,如果你不想深入理解什么是高光网格材质,什么是基于物理的材质,每种材质对应的着色器代码应该如何编写,这种情况下,你只需要会选择使用哪种网格材质就可以。

如果3D美术烘培的时候是次时代流程,也就是贴图中你可以看到高光贴图 .specularMap,你需要选择高光网格材质 MeshPhongMaterial 渲染该模型,如果3D美术烘培的时候是PBR流程,也就是贴图中你可以看到金属度贴图 .metalnessMap 和粗糙度贴图 .roughnessMap,你需要选择基于物理的材质 MeshStandardMaterial或 MeshPhysicalMaterial 解析渲染。

质感
如果展示一个物体,需要很好的质感,比如轿车、珠宝等产品展示,可以让 3D 美术选择 PBR 流程烘培导出贴图,程序员使用基于物理的材质 MeshStandardMaterial 或 MeshPhysicalMaterial 进行解析渲染。

var scene, camera, renderer, envMap, phongMaterial, standardMaterial, params1, params2, faceNormalsHelper, vertexNormalsHelper;

init();

function init()
  const assetPath = 'https://your path/';
  
  envMap = new THREE.CubeTextureLoader().setPath(`$assetPathskybox3_`).load([
    'px.jpg', 'nx.jpg', 
    'py.jpg', 'ny.jpg', 
    'pz.jpg', 'nz.jpg'
  ])
  scene = new THREE.Scene();
  scene.background = envMap;
  
  camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
  camera.position.set(0, 0, 10);
  
  const ambient = new THREE.HemisphereLight(0xffffbb, 0x080820);
  scene.add(ambient);
  
  const light = new THREE.DirectionalLight(0xFFFFFF, 3);
  light.position.set(0,4,4);
  scene.add(light);
  
  const albedoMap = new THREE.TextureLoader().load(`$assetPathTexturesCom_Orange_512_albedo.jpg`);
  const normalMap = new THREE.TextureLoader().load(`$assetPathTexturesCom_Orange_512_normal.jpg`);
  
  renderer = new THREE.WebGLRenderer();
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );
  
  const controls = new THREE.OrbitControls( camera, renderer.domElement );
  
  //Add meshes here
  const geometry = new THREE.SphereGeometry(1, 30, 20);
  phongMaterial = new THREE.MeshPhongMaterial();
  standardMaterial = new THREE.MeshStandardMaterial();
  
  const phongSphere = new THREE.Mesh( geometry, phongMaterial);
  const standardSphere = new THREE.Mesh( geometry, standardMaterial);
  
  for(let xPos=-3; xPos<3; xPos+=3)
    const sphereA = phongSphere.clone();
    sphereA.position.set(xPos, 1.5, 0);
    scene.add(sphereA);
    
    if (xPos==0)
      faceNormalsHelper = new THREE.FaceNormalsHelper(sphereA, 0.25);
      vertexNormalsHelper = new THREE.VertexNormalsHelper(sphereA, 0.25);
      faceNormalsHelper.visible = false;
      vertexNormalsHelper.visible = false;
      scene.add(faceNormalsHelper);
      scene.add(vertexNormalsHelper);
    
    
    const sphereB = standardSphere.clone();
    sphereB.position.set(xPos, -1.5, 0);
    scene.add(sphereB);
  
  
  params1 = 
    color: 0xffffff,
    envMap: 'none',
    reflectivity: 1,
    albedoMap: 'none',
    normalMap: 'none',
    normalScale: 1,
    shininess: 30,
    facetted: false,
    normals: 'none'
  
  params2 = 
    color: 0xffffff,
    emissive: 0,
    envMap: 'none',
    reflectivity: 1,
    albedoMap: 'none',
    normalMap: 'none',
    normalScale: 1,
    roughness: 0.5,
    metalness: 0.5,
    facetted: false
  
  
  const gui = new dat.gui.GUI();
  
  gui.add(params1, 'normals', ['none', 'face', 'vertex']).onChange(function(value)
    faceNormalsHelper.visible = false;
    vertexNormalsHelper.visible = false;
    phongMaterial.wireframe = false;
    switch(value)
      case 'face':
        faceNormalsHelper.visible = true;
        phongMaterial.wireframe = true;
        break;
      case 'vertex':
        vertexNormalsHelper.visible = true;
        phongMaterial.wireframe = true;
        break;
    
  );
  const f1 = gui.addFolder('Phong Material');
  f1.addColor(params1, 'color').onChange( function()  phongMaterial.color.set( params1.color );  );
  f1.add(params1, 'envMap', ['none', 'cathedral']).onChange( function()
    switch (params1.envMap)
      case 'cathedral':
        phongMaterial.envMap = envMap;
        break;
      default:
        phongMaterial.envMap = null;
        break;
    
    phongMaterial.needsUpdate = true;
  );
  f1.add(params1, 'reflectivity').min(0).max(1).step(0.01).onChange( function() phongMaterial.reflectivity = params1.reflectivity );
  f1.open();
  f1.add(params1, 'albedoMap', ['none', 'orange']).onChange( function(value)
    switch (value)
      case 'orange':
        phongMaterial.map = albedoMap;
        break;
      default:
        phongMaterial.map = null;
        break;
    
    phongMaterial.needsUpdate = true;
  );
  f1.add(params1, 'normalMap', ['none', 'dimples']).onChange( function(value)
    switch (value)
      case 'dimples':
        phongMaterial.normalMap = normalMap;
        break;
      default:
        phongMaterial.normalMap = null;
        break;
    
    phongMaterial.needsUpdate = true;
  );
  f1.add(params1, 'normalScale').min(0).max(1).step(0.01).onChange( function(value) phongMaterial.normalScale.x = value;
phongMaterial.normalScale.y = value;                                );
  f1.add(params1, 'shininess').min(0).max(255).step(0.5).onChange( 以上是关于threejs透明贴图如何不影响内部材质的主要内容,如果未能解决你的问题,请参考以下文章

threejs color 和 emissive 有啥区别

threejs 粒子系统和材质贴图

three.js(4)-网格Lambert材质

three.js(11)-纹理贴图

three.js如何加载带材质的obj外部模型

threejs里怎么解决网格贴图闪烁