SphereBufferGeometry 上的 THREE.js ShaderMaterial UV 包裹问题
Posted
技术标签:
【中文标题】SphereBufferGeometry 上的 THREE.js ShaderMaterial UV 包裹问题【英文标题】:THREE.js ShaderMaterial UV wrapping issues on SphereBufferGeometry 【发布时间】:2021-12-13 01:46:21 【问题描述】:我正在尝试用 ShaderMaterial 包裹 SphereBufferGeometry,我在其中使用噪声来模拟木星的表面,但它非常奇怪地包裹到球体几何体。所有动画纹理都出现在一条纬线周围的细带中,而不是像普通纹理那样缠绕在“行星”周围。我在下面附上了图片。
它在平面上运行良好,但我可能天真地认为它会像纹理一样简单地包裹,而且我对着色器编程很陌生,所以我有点卡住了。
this is the plane which is wrapping fine
this is not wrapping correctly
我有一种感觉,也许我可以将噪声方程移动到 fragmentShader - 但我的知识还不存在,当我尝试时它坏了。我什至尝试将平面的目标变形为球体,但 ShaderMaterial 本身并不支持 morphTargets,而且我在尝试使用 onBeforeCompile
注入 #include <morphtarget_pars_vertex>
之后仍然无法正常工作。我还尝试了 THREE 在统一纹理上的包裹方程,但它产生了类似的结果。
这是我所有的代码,shaderMaterial 实现在addPlanet()
:
import * as THREE from '../../build/three.module';
import OrbitControls from '../../examples/jsm/controls/OrbitControls';
const displacementVert = `
precision mediump float;
varying vec2 vUv;
varying float vWave;
uniform float uTime;
//
// Description : Array and textureless GLSL 2D/3D/4D simplex
// noise functions.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : ijm
// Lastmod : 20110822 (ijm)
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
//
vec3 mod289(vec3 x)
return x - floor(x * (1.0 / 289.0)) * 289.0;
vec4 mod289(vec4 x)
return x - floor(x * (1.0 / 289.0)) * 289.0;
vec4 permute(vec4 x)
return mod289(((x*34.0)+1.0)*x);
vec4 taylorInvSqrt(vec4 r)
return 1.79284291400159 - 0.85373472095314 * r;
float snoise(vec3 v)
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
// Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
void main()
vUv = uv;
vec3 pos = position;
float noiseFreq = 3.5;
float noiseAmp = 0.15;
vec3 noisePos = vec3(pos.x * noiseFreq + uTime, pos.y, pos.z);
pos.z += snoise(noisePos) * noiseAmp;
vWave = pos.z;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.);
`;
const displacementFrag = `
precision mediump float;
varying vec2 vUv;
varying float vWave;
uniform sampler2D uTexture;
void main()
float wave = vWave * 0.25;
vec3 texture = texture2D(uTexture, vUv + wave).rgb;
gl_FragColor = vec4(texture, 1.);
`;
let width, height;
let scene, camera, renderer;
let controls;
let wireframe;
let clock;
let planetShaderMaterial;
let jupiterSphere;
const init = ( params ) =>
colors = params.colors;
model = params.model;
width = params.width;
height = params.height;
scene = new THREE.Scene();
clock = new THREE.Clock();
camera = new THREE.PerspectiveCamera( params.fov, width / height, params.near, params.far );
camera.position.set( params.cameraPos.x, params.cameraPos.y, params.cameraPos.z );
renderer = new THREE.WebGLRenderer( antialias: true, logarithmicDepthBuffer: true );
renderer.setSize( width, height );
renderer.outputEncoding = THREE.sRGBEncoding;
wireframe = params.wireframe;
renderer.render( scene, camera );
controls = new OrbitControls( camera, renderer.domElement );
addLights();
addPlanet();
const addLights = () =>
const ambientLight = new THREE.AmbientLight( 0xffffff, 10 );
scene.add( ambientLight );
const dir = 1024;
const light = new THREE.DirectionalLight( 0xffffff, 1 );
light.position.set( 100, 100, 50 );
light.castShadow = true;
light.shadow.camera.left = -dir;
light.shadow.camera.right = dir;
light.shadow.camera.top = dir;
light.shadow.camera.bottom = -dir;
light.shadow.camera.near = 0.1;
light.shadow.camera.far = 1000;
light.shadow.mapSize.x = 1024;
light.shadow.mapSize.y = 1024;
scene.add( light );
// ******** HERE'S THE ShaderMaterial implementation
const addPlanet = () =>
const texture = new THREE.TextureLoader().load( './assets/textures/disp/jupiter.jpg' );
planetShaderMaterial = new THREE.ShaderMaterial(
uniforms:
uTime: value: 0.0 ,
uTexture: value: texture
,
wireframe: false,
side: THREE.FrontSide,
vertexShader: displacementVert,
fragmentShader: displacementFrag,
);
// these have no effect. Repeat Wrapping just repeats the current effect
planetShaderMaterial.uniforms.uTexture.value.wrapS = THREE.ClampToEdgeWrapping;
planetShaderMaterial.uniforms.uTexture.value.wrapT = THREE.ClampToEdgeWrapping;
jupiterSphere = new THREE.Mesh( new THREE.SphereBufferGeometry( 25, 32, 32), planetShaderMaterial );
scene.add( jupiterSphere );
const render = () =>
planetShaderMaterial.uniforms.uTime.value = clock.getElapsedTime();
renderer.render( scene, camera );
const resize = ( width, height ) =>
windowWidth = width;
windowHeight = height;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
const getRenderer = () =>
return renderer;
const TestWorld =
init,
render,
resize,
getRenderer
;
export default TestWorld;
【问题讨论】:
请修剪您的代码,以便更容易找到您的问题。请按照以下指南创建minimal reproducible example。 【参考方案1】:问题可能在于您的uv
位移量级。这基本上就是你的着色器正在做的事情:
vWave = pos.z;
float wave = vWave * 0.25;
vec3 texture = texture2D(uTexture, vUv + wave);
您的 SphereGeometry 的半径为 25
,因此您将根据其沿 z 轴的深度将 UV 置换 25 * 0.25
。这意味着您将获得范围约为 [-6.25, 6.25]
的 UV。
您可以重新计算该值以使其更小(请记住,UV 通常在 [0, 1]
范围内,因此 6 的位移将远远超出此范围。或者,您可以保持 UV 位移非常大,并允许纹理重复:
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
您可以在Texture docs page 中阅读有关包装的信息
【讨论】:
做到了!谢谢@Marquizzo。也很有意义,宝贝步骤!非常感谢人。 * 我试图将其标记为答案,但它说我不能,因为我是新手,但我会的:)以上是关于SphereBufferGeometry 上的 THREE.js ShaderMaterial UV 包裹问题的主要内容,如果未能解决你的问题,请参考以下文章
Three.js地球开发—5.国家边界线和国家轮廓Mesh进行组合