Removing moire patterns created by GLSL shaders

I set this minimal test case where you can easily see moire patterns created by decompiling oscillating red using a custom fragment shader ( jsfiddle ).

What is the general technique for removing such patterns using GLSL? I suppose it includes an extension of derivatives, but I never understood how to implement it. I actually have to do smoothing, I think?

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: I tried implementing a super-select , not sure if I implemented it correctly, but it doesn't seem to help too much.

+5
source share
1 answer

Unfortunately, the moire pattern here is the result of high-contrast lines approaching the Nyquist Frequency . In other words, there is no good way to get a high-contrast line with 1 or 2 pixels, smoothly move to the next pixel without introducing such artifacts, or blur the lines so that they are indistinguishable.

You mentioned the extension of derivatives, and indeed, this extension can be used to determine how quickly your UV changes in the screen space, and thus find out how much blur is needed to make out this problem under the mat. In a modified version of your own example below, I am trying to use fwidth to turn a sphere of red where the noise becomes bad. Try playing with some of the floats that are defined by constants here, see what you can find.

 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> 
+6
source

Source: https://habr.com/ru/post/1239465/


All Articles