Skip to main content

THREE.js ShaderMaterial UV wrapping issues on SphereBufferGeometry

I'm trying to wrap a SphereBufferGeometry with a ShaderMaterial where I'm using noise to resemble the surface of Jupiter, but it's wrapping very oddly to the sphere geometry. All of the animated texture appears in a thin belt around one of the lines of latitude rather than wrapped around the 'planet' like a normal texture. I've attached images below.

It works well on a plane, but I was probably naive to think it would simply wrap like a texture would wrap, and I'm quite new to Shader programming so I'm a bit stuck.

this is the plane which is wrapping fine

this is not wrapping correctly

I've a feeling that maybe I can move the noise equations to the fragmentShader - but my knowledge isn't there yet, it broke when I tried. I even tried morphing the targets of the plane into a sphere but ShaderMaterial doesn't natively support morphTargets and I after a LOT of trying to inject the #include <morphtarget_pars_vertex> using onBeforeCompile I still couldn't get it to work. I've also tried THREE's wrapping equations on the uniform texture, but it yields similar results.

Here's all of my code, the shaderMaterial implementation is in 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;
Via Active questions tagged javascript - Stack Overflow https://ift.tt/2FdjaAW

Comments

Popular posts from this blog

How to show number of registered users in Laravel based on usertype?

i'm trying to display data from the database in the admin dashboard i used this: <?php use Illuminate\Support\Facades\DB; $users = DB::table('users')->count(); echo $users; ?> and i have successfully get the correct data from the database but what if i want to display a specific data for example in this user table there is "usertype" that specify if the user is normal user or admin i want to user the same code above but to display a specific usertype i tried this: <?php use Illuminate\Support\Facades\DB; $users = DB::table('users')->count()->WHERE usertype =admin; echo $users; ?> but it didn't work, what am i doing wrong? source https://stackoverflow.com/questions/68199726/how-to-show-number-of-registered-users-in-laravel-based-on-usertype

Why is my reports service not connecting?

I am trying to pull some data from a Postgres database using Node.js and node-postures but I can't figure out why my service isn't connecting. my routes/index.js file: const express = require('express'); const router = express.Router(); const ordersCountController = require('../controllers/ordersCountController'); const ordersController = require('../controllers/ordersController'); const weeklyReportsController = require('../controllers/weeklyReportsController'); router.get('/orders_count', ordersCountController); router.get('/orders', ordersController); router.get('/weekly_reports', weeklyReportsController); module.exports = router; My controllers/weeklyReportsController.js file: const weeklyReportsService = require('../services/weeklyReportsService'); const weeklyReportsController = async (req, res) => { try { const data = await weeklyReportsService; res.json({data}) console...

How to split a rinex file if I need 24 hours data

Trying to divide rinex file using the command gfzrnx but getting this error. While doing that getting this error msg 'gfzrnx' is not recognized as an internal or external command Trying to split rinex file using the command gfzrnx. also install'gfzrnx'. my doubt is I need to run this program in 'gfzrnx' or in 'cmdprompt'. I am expecting a rinex file with 24 hrs or 1 day data.I Have 48 hrs data in RINEX format. Please help me to solve this issue. source https://stackoverflow.com/questions/75385367/how-to-split-a-rinex-file-if-i-need-24-hours-data