javascript - SphereBufferGeometry 上的 THREE.js ShaderMaterial UV 包裹问题
问题描述
我正在尝试用 ShaderMaterial 包裹 SphereBufferGeometry,我使用噪声来模拟木星的表面,但它非常奇怪地包裹到球体几何形状。所有动画纹理都出现在一条纬线周围的细带中,而不是像普通纹理那样缠绕在“行星”周围。我在下面附上了图片。
它在平面上运行良好,但我可能天真地认为它会像纹理一样简单地包裹,而且我对着色器编程很陌生,所以我有点卡住了。
我有一种感觉,也许我可以将噪声方程移动到 fragmentShader - 但我的知识还不存在,当我尝试时它坏了。我什至尝试将平面的目标变形为球体,但 ShaderMaterial 本身并不支持 morphTargets,而且我在尝试注入#include <morphtarget_pars_vertex>
using之后onBeforeCompile
仍然无法正常工作。我还尝试了 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;
解决方案
问题可能在于您的uv
位移量。这基本上就是你的着色器正在做的事情:
vWave = pos.z;
float wave = vWave * 0.25;
vec3 texture = texture2D(uTexture, vUv + wave);
您的 SphereGeometry 的半径为25
,因此您将25 * 0.25
根据其沿 z 轴的深度将 UV 移动 。这意味着您获得的 UV 范围约为[-6.25, 6.25]
.
您可以重新计算此值以使其更小(请记住,UV 通常在该[0, 1]
范围内,因此 6 的位移将远远超出此范围。或者,您可以保持 UV 位移非常大,并允许纹理重复:
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
您可以在Texture docs 页面中阅读有关包装的信息
推荐阅读
- java - 无法为事务打开 Hibernate Session;嵌套异常是 org.hibernate.exception
- hibernate - 在@Transactional注解方法下获取当前事务
- processing - 在处理中使用光线进行碰撞检测的最佳方法是什么?
- regex - 如何查找所有 *=* 并将其替换为 * = *
- r - 如何调整随机森林代码以进行质量预测
- jquery - 如何以循环的形式在画布中绘制多个图像?
- graphql - 如何在 Gatsbyjs 中正确设置和查询 siteMetadata?
- apache - 让我们在 AWS Lightsail 的 bitnami.conf 中加密 SSL 设置
- mongoose - Mongoose:如何以编程方式重新定义模式?
- javascript - 如何以编程方式从 jQuery 布局中的 Resizer 栏触发调整大小?