2015-12-30 8 views
5

Ich habe diesen minimalen Testfall eingerichtet, in dem Sie die Moiré-Muster durch Unterabtastung der oszillierenden roten Farbe mit einem benutzerdefinierten Fragment-Shader (jsfiddle) leicht erkennen können.Entfernen von Moiré-Mustern, die von GLSL-Shadern erzeugt werden

Was ist die allgemeine Technik zum Entfernen solcher Muster mit GLSL? Ich nehme an, dass es die Derivaterweiterung betrifft, aber ich habe nie verstanden, wie man es implementiert. Ich muss im Grunde Anti-Aliasing machen, denke ich?

var canvas = document.getElementById('canvas'); 
 
var scene = new THREE.Scene(); 
 
var renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true}); 
 
var camera = new THREE.PerspectiveCamera(75, canvas.clientWidth/canvas.clientWidth, 1, 1000); 
 

 
var geometry = new THREE.SphereGeometry(50, 50, 50); 
 
var material = new THREE.ShaderMaterial({ 
 
    vertexShader: document.getElementById('vertex-shader').textContent, 
 
    fragmentShader: document.getElementById('fragment-shader').textContent 
 
}); 
 
var sphere = new THREE.Mesh(geometry, material); 
 

 
scene.add(sphere); 
 

 
camera.position.z = 100; 
 

 
var period = 30; 
 
var clock = new THREE.Clock(); 
 
render(); 
 

 
function render() { 
 
    requestAnimationFrame(render); 
 
    
 
    if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) { 
 
    renderer.setSize(canvas.clientWidth, canvas.clientHeight, false); 
 
    camera.aspect = canvas.clientWidth/canvas.clientHeight; 
 
    camera.updateProjectionMatrix(); 
 
    } 
 
    
 
    sphere.rotation.y -= clock.getDelta() * 2 * Math.PI/period; 
 
    renderer.render(scene, camera); 
 
}
html, body, #canvas { 
 
    margin: 0; 
 
    padding: 0; 
 
    width: 100%; 
 
    height: 100%; 
 
    overflow: hidden; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.min.js"></script> 
 
<canvas id="canvas"></canvas> 
 
<script id="vertex-shader" type="x-shader/x-vertex"> 
 
    varying vec2 vUv; 
 

 
    void main() { 
 
    vUv = uv; 
 
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 
 
    } 
 
</script> 
 
<script id="fragment-shader" type="x-shader/x-fragment"> 
 
    #define M_TAU 6.2831853071795864769252867665590 
 

 
    varying vec2 vUv; 
 

 
    void main() { 
 
    float w = sin(500.0 * M_TAU * vUv.x)/2.0 + 0.5; 
 
    vec3 color = vec3(w, 0.0, 0.0); 
 
    gl_FragColor = vec4(color, 1.0); 
 
    } 
 
</script>

Update: Ich habe versucht, super-sampling, nicht sicher zu implementieren, wenn ich es richtig umgesetzt haben, aber es scheint nicht zu viel zu helfen.

+0

@ WacławJasper die uv-Koordinaten zum Runterladen Verwendung war nur ein einfacher Weg, um diesen Effekt zu erhalten. Die Naht selbst verursacht das Problem nicht, sonst würden Sie nur das Problem um die Naht selbst sehen. –

Antwort

6

Leider ist das Moiré-Muster hier eine Folge der kontrastreichen Linien, die sich der Nyquist Frequency nähern. Mit anderen Worten, es gibt keinen guten Weg, um eine 1 oder 2 Pixel breite kontrastreiche Linie sanft zum nächsten Pixel zu verschieben, ohne entweder solche Artefakte einzuführen oder die Linien zu verwischen, um nicht unterscheidbar zu sein.

Sie erwähnten die Derivate-Erweiterung, und in der Tat kann diese Erweiterung verwendet werden, um herauszufinden, wie schnell Ihre UVs im Bildschirmraum ändern, und herauszufinden, wie viel Unschärfe benötigt wird, um dieses Problem unter den Teppich zu kehren. In der modifizierten Version Ihres eigenen Beispiels unten versuche ich fwidth zu verwenden, um die Kugel rot zu machen, wo das Rauschen schlecht wird. Versuchen Sie, mit einigen der Floats zu spielen, die hier als Konstanten definiert sind, sehen Sie, was Sie finden können.

var canvas = document.getElementById('canvas'); 
 
var scene = new THREE.Scene(); 
 
var renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true}); 
 
var camera = new THREE.PerspectiveCamera(75, canvas.clientWidth/canvas.clientWidth, 1, 1000); 
 

 
var geometry = new THREE.SphereGeometry(50, 50, 50); 
 
var material = new THREE.ShaderMaterial({ 
 
    vertexShader: document.getElementById('vertex-shader').textContent, 
 
    fragmentShader: document.getElementById('fragment-shader').textContent 
 
}); 
 
var sphere = new THREE.Mesh(geometry, material); 
 

 
scene.add(sphere); 
 

 
camera.position.z = 100; 
 

 
var period = 30; 
 
var clock = new THREE.Clock(); 
 
render(); 
 

 
function render() { 
 
    requestAnimationFrame(render); 
 
    
 
    if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) { 
 
    renderer.setSize(canvas.clientWidth, canvas.clientHeight, false); 
 
    camera.aspect = canvas.clientWidth/canvas.clientHeight; 
 
    camera.updateProjectionMatrix(); 
 
    } 
 
    
 
    sphere.rotation.y -= clock.getDelta() * 2 * Math.PI/period; 
 
    renderer.render(scene, camera); 
 
}
html, body, #canvas { 
 
    margin: 0; 
 
    padding: 0; 
 
    width: 100%; 
 
    height: 100%; 
 
    overflow: hidden; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.min.js"></script> 
 
<canvas id="canvas"></canvas> 
 
<script id="vertex-shader" type="x-shader/x-vertex"> 
 
    varying vec2 vUv; 
 

 
    void main() { 
 
    vUv = uv; 
 
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 
 
    } 
 
</script> 
 
<script id="fragment-shader" type="x-shader/x-fragment"> 
 
    #extension GL_OES_standard_derivatives : enable 
 

 
    #define M_TAU 6.2831853071795864769252867665590 
 

 
    varying vec2 vUv; 
 

 
    void main() { 
 
    float linecount = 200.0; 
 
    float thickness = 0.0; 
 
    float blendregion = 2.8; 
 
    
 
    // Loosely based on https://github.com/AnalyticalGraphicsInc/cesium/blob/1.16/Source/Shaders/Materials/GridMaterial.glsl#L17-L34 
 
    float scaledWidth = fract(linecount * vUv.s); 
 
    scaledWidth = abs(scaledWidth - floor(scaledWidth + 0.5)); 
 
    vec2 dF = fwidth(vUv) * linecount; 
 
    float value = 1.0 - smoothstep(dF.s * thickness, dF.s * (thickness + blendregion), scaledWidth); 
 
    gl_FragColor = vec4(value, 0.0, 0.0, 1.0); 
 
    } 
 
</script>

+0

Danke für die Antwort! Das einzige, was ich fragen könnte, ist vielleicht weitere Informationen oder Dokumentation über verschiedene Techniken für diese Art von Ding? Verflixten sie im Grunde nur die Hochfrequenz-Sachen? –

+1

Soweit ich weiß, ist das die einzige Lösung: Pixel-basierte Displays können einfach keine höheren Frequenzen als die Pixeldichte anzeigen. In dem obigen Code werden "Dicke" und "Blendregion" in Bildraumpixeln angegeben, dank "fwidth", was bedeutet, dass die Linien sich zu überlappen beginnen, wenn sie weniger als 3 Pixel voneinander entfernt sind. Weniger als das und Sie haben nur 1 Pixel für die Linie und 1 Pixel für die Lücke, und die Dinge werden unordentlich, wenn die Linien nicht auf dem Pixelraster bleiben. – emackey

+1

BTW, die Logik, auf der dieser Code basiert, wird ausführlicher im Buch "3D Engine Design for Virtual Globes" von Cozzi und Ring, Listing 4.13 beschrieben. – emackey

Verwandte Themen