Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 492ad16

Browse files
committedApr 29, 2023
Add demo for Hierarchical Sample Warping
1 parent 2a9711e commit 492ad16

File tree

7 files changed

+12226
-0
lines changed

7 files changed

+12226
-0
lines changed
 

‎README.md

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ Demo suite for LWJGL 3
2323

2424
![opengl/raytracing/tutorial/Tutorial8_2.java](md/tutorial8_2.jpg)
2525

26+
[opengl/sampling/HierarchicalSampleWarping.java](./src/org/lwjgl/demo/opengl/sampling/HierarchicalSampleWarping.java)
27+
28+
![opengl/sampling/HierarchicalSampleWarping.java](md/hsw.jpg)
29+
2630
## Building
2731

2832
./mvnw package

‎md/hsw.jpg

390 KB
Loading

‎res/org/lwjgl/demo/opengl/sampling/env-square.hdr

+9
Large diffs are not rendered by default.

‎res/org/lwjgl/demo/opengl/sampling/env-square2.hdr

+11,966
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright LWJGL. All rights reserved.
3+
* License terms: https://www.lwjgl.org/license
4+
*/
5+
#version 330 core
6+
7+
uniform sampler2D tex;
8+
uniform float time;
9+
uniform float blendFactor;
10+
11+
in vec2 texcoords;
12+
out vec4 color;
13+
14+
uvec3 pcg3d(uvec3 v) {
15+
v = v * 1664525u + 1013904223u;
16+
v.x += v.y * v.z;
17+
v.y += v.z * v.x;
18+
v.z += v.x * v.y;
19+
v ^= v >> 16u;
20+
v.x += v.y * v.z;
21+
v.y += v.z * v.x;
22+
v.z += v.x * v.y;
23+
return v;
24+
}
25+
vec3 random3(vec3 f) {
26+
return uintBitsToFloat((pcg3d(floatBitsToUint(f)) & 0x007FFFFFu) | 0x3F800000u) - vec3(1.0);
27+
}
28+
float luminance(vec3 rgb) {
29+
return dot(rgb, vec3(0.2126, 0.7152, 0.0722));
30+
}
31+
vec2 importanceSampleHierarchicalWarping(sampler2D t, vec2 u) {
32+
int x = 0, y = 0;
33+
vec2 u0 = u;
34+
ivec2 texSize = textureSize(t, 0);
35+
int maxLod = int(log2(float(texSize.x)) - 0.5);
36+
for (int lod = maxLod; lod >= 0; lod--) {
37+
x <<= 1; y <<= 1;
38+
float x0y0 = luminance(texelFetch(t, ivec2(x+0,y+0), lod).rgb);
39+
float x1y0 = luminance(texelFetch(t, ivec2(x+1,y+0), lod).rgb);
40+
float x0y1 = luminance(texelFetch(t, ivec2(x+0,y+1), lod).rgb);
41+
float x1y1 = luminance(texelFetch(t, ivec2(x+1,y+1), lod).rgb);
42+
float left = x0y0 + x0y1, right = x1y0 + x1y1, pLeft = left / (left + right);
43+
float uxFactor = step(pLeft, u.x);
44+
float pLower = mix(x0y0 / left, x1y0 / right, uxFactor);
45+
float uyFactor = step(pLower, u.y);
46+
float uxDen = mix(pLeft, 1.0 - pLeft, uxFactor), uyDen = mix(pLower, 1.0 - pLower, uyFactor);
47+
u.x = mix(u.x, u.x - pLeft, uxFactor) / uxDen; u.y = mix(u.y, u.y - pLower, uyFactor) / uyDen;
48+
x += int(uxFactor); y += int(uyFactor);
49+
}
50+
return (vec2(x, y) + u0) / vec2(texSize);
51+
}
52+
float distMetric(vec2 a, vec2 b) {
53+
return sqrt(dot(a - b, a - b));
54+
}
55+
void main(void) {
56+
vec3 c = texture(tex, texcoords).rgb;
57+
vec3 rnd = random3(vec3(texcoords, time));
58+
vec2 s = importanceSampleHierarchicalWarping(tex, rnd.xy);
59+
float dist = distMetric(texcoords, s);
60+
float distanceRandomness = 0.01 + rnd.z * 0.05;
61+
if (dist < distanceRandomness)
62+
c = vec3(15.0, 0.0, 0.0);
63+
//color = vec4(c, blendFactor);
64+
color = vec4(c, 0.5);
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Copyright LWJGL. All rights reserved.
3+
* License terms: https://www.lwjgl.org/license
4+
*/
5+
#version 330 core
6+
out vec2 texcoords;
7+
void main(void) {
8+
vec2 vertex = vec2(-1.0) + vec2(
9+
float((gl_VertexID & 1) << 2),
10+
float((gl_VertexID & 2) << 1));
11+
texcoords = vertex * 0.5 + 0.5;
12+
gl_Position = vec4(vertex, 0.0, 1.0);
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package org.lwjgl.demo.opengl.sampling;
2+
3+
import org.lwjgl.BufferUtils;
4+
import org.lwjgl.demo.opengl.util.DemoUtils;
5+
import org.lwjgl.glfw.GLFWKeyCallback;
6+
import org.lwjgl.opengl.GL;
7+
import org.lwjgl.system.MemoryStack;
8+
9+
import java.io.IOException;
10+
import java.nio.ByteBuffer;
11+
import java.nio.FloatBuffer;
12+
import java.nio.IntBuffer;
13+
14+
import static org.lwjgl.demo.util.IOUtils.ioResourceToByteBuffer;
15+
import static org.lwjgl.glfw.GLFW.*;
16+
import static org.lwjgl.opengl.GL33C.*;
17+
import static org.lwjgl.stb.STBImage.*;
18+
import static org.lwjgl.system.MemoryStack.stackPush;
19+
import static org.lwjgl.system.MemoryUtil.NULL;
20+
import static org.lwjgl.system.MemoryUtil.memAddress;
21+
22+
/**
23+
* Demo showcasing Hierarchical Sample Warping (HSW) as first proposed in the paper
24+
* "Wavelet Importance: Efficiently Evaluating Products of Complex Functions" by Clarberg et al.
25+
* <p>
26+
* This demo implements importance sampling via hierarchical sample warping in its associated fragment shader
27+
* by using a 2D lightmap texture and generating samples with probabilities proportional to a texel's luminance.
28+
* <p>
29+
* See:
30+
* <ul>
31+
* <li><a href="https://link.springer.com/content/pdf/10.1007/978-1-4842-4427-2_16.pdf">section 16.4.2.3 HIERARCHICAL TRANSFORMATION in chapter "Transformations Zoo" of "Ray Tracing Gems".</a></li>
32+
* <li><a href="http://graphics.ucsd.edu/~henrik/papers/wavelet_importance_sampling.pdf">"Wavelet Importance: Efficiently Evaluating Products of Complex Functions" by Clarberg et al.</a></li>
33+
* <li><a href="https://www.ea.com/seed/news/siggraph21-global-illumination-surfels">SIGGRAPH 21: Global Illumination Based on Surfels</a></li>
34+
* </ul>
35+
*
36+
* @author Kai Burjack
37+
*/
38+
public class HierarchicalSampleWarping {
39+
private static int width = 800, height = 800;
40+
public static void main(String[] args) throws IOException {
41+
if (!glfwInit())
42+
throw new IllegalStateException("Unable to initialize GLFW");
43+
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
44+
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
45+
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
46+
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
47+
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
48+
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
49+
long window = glfwCreateWindow(width, height, "Hello HSW!", NULL, NULL);
50+
if (window == NULL)
51+
throw new RuntimeException("Failed to create the GLFW window");
52+
glfwSetKeyCallback(window, new GLFWKeyCallback() {
53+
public void invoke(long window, int key, int scancode, int action, int mods) {
54+
if (action != GLFW_RELEASE)
55+
return;
56+
if (key == GLFW_KEY_ESCAPE)
57+
glfwSetWindowShouldClose(window, true);
58+
}
59+
});
60+
glfwMakeContextCurrent(window);
61+
glfwSwapInterval(1);
62+
GL.createCapabilities();
63+
64+
// Read actualy framebuffer size for HiDPI displays
65+
try (MemoryStack stack = stackPush()) {
66+
IntBuffer framebufferSize = stack.mallocInt(2);
67+
nglfwGetFramebufferSize(window, memAddress(framebufferSize), memAddress(framebufferSize) + 4);
68+
width = framebufferSize.get(0);
69+
height = framebufferSize.get(1);
70+
}
71+
72+
// Create shader program which implements visualization of the hierarchical sample warping
73+
int program = glCreateProgram();
74+
{
75+
int vshader = DemoUtils.createShader("org/lwjgl/demo/opengl/sampling/hsw.vs.glsl", GL_VERTEX_SHADER, null);
76+
int fshader = DemoUtils.createShader("org/lwjgl/demo/opengl/sampling/hsw.fs.glsl", GL_FRAGMENT_SHADER, null);
77+
glAttachShader(program, vshader);
78+
glAttachShader(program, fshader);
79+
glBindFragDataLocation(program, 0, "color");
80+
glLinkProgram(program);
81+
int linked = glGetProgrami(program, GL_LINK_STATUS);
82+
String programLog = glGetProgramInfoLog(program);
83+
if (programLog.trim().length() > 0)
84+
System.err.println(programLog);
85+
if (linked == 0)
86+
throw new AssertionError("Could not link program");
87+
}
88+
glUseProgram(program);
89+
glUniform1i(glGetUniformLocation(program, "tex"), 0);
90+
int timeLocation = glGetUniformLocation(program, "time");
91+
int blendFactorLocation = glGetUniformLocation(program, "blendFactor");
92+
93+
// Create empty VAO
94+
int vao = glGenVertexArrays();
95+
glBindVertexArray(vao);
96+
97+
// Create FBO with 32-bit floating point color buffer to do the sample accumulation
98+
int fbo = glGenFramebuffers();
99+
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
100+
int framebufferTex = glGenTextures();
101+
{
102+
glBindTexture(GL_TEXTURE_2D, framebufferTex);
103+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, (ByteBuffer) null);
104+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
105+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
106+
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferTex, 0);
107+
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
108+
throw new AssertionError("Incomplete framebuffer");
109+
}
110+
111+
// Load an image, that we want to sample via hierarchical sample warping, into a texture
112+
// and generate mipmaps for it
113+
int tex = glGenTextures();
114+
{
115+
glBindTexture(GL_TEXTURE_2D, tex);
116+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
117+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
118+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
119+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
120+
IntBuffer w = BufferUtils.createIntBuffer(1);
121+
IntBuffer h = BufferUtils.createIntBuffer(1);
122+
IntBuffer comp = BufferUtils.createIntBuffer(1);
123+
ByteBuffer imageBuffer = ioResourceToByteBuffer("org/lwjgl/demo/opengl/sampling/env-square2.hdr", 64 * 1024);
124+
stbi_set_flip_vertically_on_load(true);
125+
if (!stbi_info_from_memory(imageBuffer, w, h, comp))
126+
throw new IOException("Failed to read image information: " + stbi_failure_reason());
127+
FloatBuffer image = stbi_loadf_from_memory(imageBuffer, w, h, comp, 3);
128+
if (image == null)
129+
throw new IOException("Failed to load image: " + stbi_failure_reason());
130+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, w.get(0), h.get(0), 0, GL_RGB, GL_FLOAT, (ByteBuffer) null);
131+
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w.get(0), h.get(0), GL_RGB, GL_FLOAT, image);
132+
stbi_image_free(image);
133+
glGenerateMipmap(GL_TEXTURE_2D);
134+
}
135+
136+
glfwShowWindow(window);
137+
138+
long lastTime = System.nanoTime();
139+
float time = 0.0f;
140+
int frameNumber = 0;
141+
142+
// Enable blending for sample accumulation in the FBO
143+
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
144+
glEnable(GL_BLEND);
145+
146+
while (!glfwWindowShouldClose(window)) {
147+
glfwPollEvents();
148+
149+
long thisTime = System.nanoTime();
150+
float elapsed = (thisTime - lastTime) / 1E9f;
151+
time += elapsed;
152+
lastTime = thisTime;
153+
154+
glViewport(0, 0, width, height);
155+
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
156+
glUseProgram(program);
157+
glBindTexture(GL_TEXTURE_2D, tex);
158+
glUniform1f(blendFactorLocation, frameNumber / (frameNumber + 1.0f));
159+
glUniform1f(timeLocation, time);
160+
glDrawArrays(GL_TRIANGLES, 0, 3);
161+
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
162+
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
163+
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
164+
165+
glfwSwapBuffers(window);
166+
frameNumber++;
167+
}
168+
}
169+
}

0 commit comments

Comments
 (0)
Please sign in to comment.