简体   繁体   中英

Improve UV line quality of threejs/shader geometry

I'm using the uv output of threejs's torus to create moving lines across the torus. It works, but doesn't look crisp.

How can I improve the line quality? I've tried making the material two-sided, and increasing the width of the lines, but the quality isn't improving much.

I haven't tried completely reproducing the torus outside of threejs, but that's out of my comfort zone.

I'm hoping there's way to change the logic of the fragment shader to produce clearer lines. I'd be greatful for any suggestions.

Codepen

/* Scene Initialization */
var startTime = Date.now();
var scene = new THREE.Scene();
var width = window.innerWidth;
var height = window.innerHeight;
var canvas = document.getElementById('canvas');
var camera = new THREE.PerspectiveCamera(75, 1, 1, 1200);
// var camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 1200 );

camera.position.set(0, -420, 600);
camera.lookAt(new THREE.Vector3(0, 0, 0));

var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth * .2, window.innerWidth * .2);
renderer.setClearColor( 0xffffff, 1);
canvas.appendChild(renderer.domElement);

var geometry = new THREE.TorusGeometry(200, 200, 260, 260); 
material = new THREE.ShaderMaterial( {
        uniforms: {time: { type: "f", value: Date.now() - startTime}, },
        vertexShader: `attribute vec3 center;
            varying vec3 vCenter;
      varying vec2 vUv;
            void main() {
                vCenter = center;
        vUv = uv;
                gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
            }`,
        fragmentShader: `varying vec3 vCenter;
      varying vec2 vUv;
      uniform float time;
      uniform sampler2D tDiffuse;
            void main() {
        float sh = 0.005;
        float PI = 3.1415926535897932384626433832795;
        // float linesX = mod(time + vUv.x, 0.03);
        float linesX = sin((time + vUv.x) * PI * 30.)/30.;
        // float linesY = mod(time + vUv.y, 0.05);
        float linesY = sin((time + vUv.y) * PI * 20.)/20.;
        float smoothX =
        smoothstep( 0.0 - sh, 0.0, linesX) -
          smoothstep( 0.0, 0.0 + sh, linesX);
        float smoothY =
        smoothstep( 0.0 - sh, 0.0, linesY) -
          smoothstep( 0.0, 0.0 + sh, linesY);

        float uvOutput = smoothX + smoothY;
        gl_FragColor.rgb = vec3(1.0, 0, 0);
        gl_FragColor.a = uvOutput;
        // gl_FragColor = vec4(1.,0,0,1.)

            }`
      } );
//material.extensions.derivatives = true;

material.side = THREE.DoubleSide;

material.transparent = true;

//material.blending = THREE.Add;
material.depthTest = false;

var torus = new THREE.Mesh(geometry, material);
var geom = torus.geometry;
geometry.sortFacesByMaterialIndex();
torus.position.x = 0;

scene.add(torus);

/* Request Animation Frame */
function animation() {
  camera.lookAt(new THREE.Vector3(0, 0, 0));
  renderer.render(scene, camera);
  material.uniforms.time.value = (Date.now() - startTime)/20000;
  requestAnimationFrame(animation);
}

animation();
setupDraggableEvents();

function setupDraggableEvents() {
  var hammer = new Hammer(document.getElementsByTagName('canvas')[0]);
  hammer.on('pan', function(event) {
    torus.rotation.y += event.velocityX / 10;
    torus.rotation.x += event.velocityY / 10;
  });
}

I recommend to define the number of lines for both directions and to calculate the distance to a line in terms of UV coordinates:

float t = time;

vec2 noLines = vec2(30.0, 20.0);
vec2 floorUV = floor((t + vUv) * noLines);
vec2 distUV  = t + vUv - (floorUV+0.5) / noLines;

Smoothly interpolate between the thickness and the half thickness of a line ( smoothstep ), to calculate the "saturation". This causes that the line always has the full "strength" in the middle (of course you can experiment with this eg sh*0.66 , sh*0.33 ):

float sh = 0.005;
vec2 lineUV = smoothstep(sh, sh*0.5, abs(distUV));

The alpha channel is the maximum "saturation" value of both directions:

float uvOutput = max(lineUV.x, lineUV.y);
gl_FragColor = vec4(1.0, 0.0, 0.0, uvOutput);

See the example, where I applied the suggested changes to your original code:

 /* Scene Initialization */ var startTime = Date.now(); var scene = new THREE.Scene(); var width = window.innerWidth; var height = window.innerHeight; var canvas = document.getElementById('canvas'); var camera = new THREE.PerspectiveCamera(75, 1, 1, 1200); camera.position.set(0, -420, 600); camera.lookAt(new THREE.Vector3(0, 0, 0)); orbitControls = new THREE.OrbitControls(camera); var renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize(window.innerWidth * .2, window.innerWidth * .2); renderer.setClearColor( 0xffffff, 1); canvas.appendChild(renderer.domElement); var geometry = new THREE.TorusGeometry(200, 200, 260, 260); material = new THREE.ShaderMaterial( { uniforms: {time: { type: "f", value: Date.now() - startTime}, }, vertexShader: `attribute vec3 center; varying vec3 vCenter; varying vec2 vUv; void main() { vCenter = center; vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }`, fragmentShader: `varying vec3 vCenter; varying vec2 vUv; uniform float time; uniform sampler2D tDiffuse; void main() { float t = time; vec2 noLines = vec2(30.0, 20.0); vec2 floorUV = floor((t + vUv) * noLines); vec2 distUV = t + vUv - (floorUV+0.5) / noLines; float sh = 0.005; vec2 lineUV = smoothstep(sh, sh*0.5, abs(distUV)); float uvOutput = max(lineUV.x, lineUV.y); gl_FragColor = vec4(1.0, 0.0, 0.0, uvOutput); }`, transparent: true } ); //material.extensions.derivatives = true; material.side = THREE.DoubleSide; material.transparent = true; //material.blending = THREE.Add; material.depthTest = false; var torus = new THREE.Mesh(geometry, material); var geom = torus.geometry; geometry.sortFacesByMaterialIndex(); torus.position.x = 0; scene.add(torus); /* Request Animation Frame */ function animation() { camera.lookAt(new THREE.Vector3(0, 0, 0)); renderer.render(scene, camera); material.uniforms.time.value = (Date.now() - startTime)/20000; requestAnimationFrame(animation); } resize(); window.onresize = resize; animation(); setupDraggableEvents(); function setupDraggableEvents() { var hammer = new Hammer(document.getElementsByTagName('canvas')[0]); hammer.on('pan', function(event) { torus.rotation.y += event.velocityX / 10; torus.rotation.x += event.velocityY / 10; }); } function resize() { var aspect = window.innerWidth / window.innerHeight; renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = aspect; camera.updateProjectionMatrix(); } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script> <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script> <div id="canvas"></div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM