提高Threejs /着色器几何图形的UV线质量

时间:2019-01-03 02:35:27

标签: javascript three.js glsl shader

我正在使用threejs圆环的uv输出在圆环上创建移动线。它可以工作,但看起来不清晰。

如何改善线路质量? 我尝试将材料制作成双面,并增加线的宽度,但是质量并没有太大改善。

我还没有尝试在Threejs之外完全复制圆环,但这超出了我的舒适范围。

我希望可以改变片段着色器的逻辑以产生更清晰的线条。我会很乐意提供任何建议。

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;
  });
}

1 个答案:

答案 0 :(得分:0)

我建议定义两个方向的线数,并根据UV坐标计算到线的距离:

float t = time;

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

在线条的粗细和一半粗细之间{smoothstep)平滑插值,以计算“饱和度”。这导致该行始终始终在中间具有完整的“强度”(当然,您可以尝试使用此sh*0.66sh*0.33):

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

alpha通道是两个方向的最大“饱和​​度”值:

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

请参见示例,其中我将建议的更改应用于原始代码:

/* 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>