Last active
April 11, 2022 18:40
-
-
Save SpineyPete/f0ba818865f6e9d4cffa5caae6eac695 to your computer and use it in GitHub Desktop.
Concentric Hemisphere Point Generator (Processing 3 Sketch)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Visualization of Shirley and Chui's Concentric Mapping | |
// projected onto a hemisphere -- e.g. for irradiance tracing. | |
// The points are drawn to the window and C code is output to the console. | |
// This import is needed since Processing 4. | |
// In Processing complains about it not finding it: | |
// In the menubar go to to sketch -> import library -> add library, | |
// enter javafx in the searchbox and download it. | |
import processing.javafx.*; | |
// =========================================================== | |
// The amount of points is the square of this number | |
int POINTS_PER_AXIS = 16; | |
// Whether to generate a cosine or uniform covering | |
// of the hemisphere. | |
boolean COSINE_DISTRIBUTION = true; | |
// Whether to skip the ring at the base of the hemisphere. | |
// (where cos(theta) == 0) | |
boolean SKIP_BASE_RING = true; | |
// =========================================================== | |
void circle(float x, float y, float r) { | |
ellipse(x, y, r*2, r*2); | |
} | |
void xcross(float x, float y, float r) { | |
line(x-r, y-r, x+r, y+r); | |
line(x+r, y-r, x-r, y+r); | |
} | |
void setup() { | |
size(600, 600, FX2D); | |
background(255); | |
noLoop(); | |
} | |
PVector MapToConcentricHemisphere(float s, float t, boolean isCosine) { | |
// Adapted from "A Low Distortion Map Between Disk and Square" | |
// By Peter Shirley and Kenneth Chiu | |
assert(s >= 0.0 && s <= 1.0); | |
assert(t >= 0.0 && t <= 1.0); | |
// Avoid float and sqrt() problems | |
if (s == 0.0) { s = 0.0001; } | |
else if (s == 1.0) { s = 0.9999; } | |
if (t == 0.0) { t = 0.0001; } | |
else if (t == 1.0) { t = 0.9999; } | |
// First check which quadrant the input coordinate | |
// lies in, map it to it's concentric equivalent in | |
// polar coordinates, with phi r and theta phi. | |
float phi, theta; | |
float a = 2*s - 1; | |
float b = 2*t - 1; | |
if (a > -b) { | |
if (a > b) { | |
// Quadrant 1, also abs(a) > abs(b). | |
phi = a; | |
theta = (PI / 4) * (b / a); | |
} else { | |
// Quadrant 2, also abs(b) > abs(a). | |
phi = b; | |
theta = (PI / 4) * (2 - (a / b)); | |
} | |
} else { | |
if (a < b) { | |
// Quadrant 3, also abs(a) >= abs(b) and a != 0. | |
phi = -a; | |
theta = (PI/4) * (4 + (b/a)); | |
} else { | |
// Quadrant 4, also abs(b) >= abs(a), | |
// but a == 0 and b == 0 could occur. | |
phi = -b; | |
theta = b == 0 ? 0 : (PI/4) * (6 - (a/b)); | |
} | |
} | |
// Convert to cartesian coordinates. | |
float u, v; | |
u = phi * cos(theta); | |
v = phi * sin(theta); | |
// Project from disk to hemisphere. | |
float x, y, z, r; | |
r = sqrt(u*u + v*v); | |
if (isCosine) { | |
x = phi * cos(theta); | |
y = phi * sin(theta); | |
z = sqrt(1.0 - r*r); | |
} else { // uniform | |
x = u * sqrt(2.0 - r*r); | |
y = v * sqrt(2.0 - r*r); | |
z = 1.0 - r*r; | |
} | |
return new PVector(x, y, z); | |
} | |
ArrayList<PVector> ConcentricHemisphere(int points_per_axis, boolean isCosine, boolean skip) { | |
assert(points_per_axis > 1); | |
ArrayList<PVector> result = new ArrayList(); | |
int begin, end; | |
if (skip) { | |
points_per_axis += 1; | |
begin = 1; | |
end = points_per_axis; | |
} else { | |
points_per_axis -= 1; | |
begin = 0; | |
end = points_per_axis + 1; | |
} | |
for (int y = begin; y < end; y++) { | |
for (int x = begin; x < end; x++) { | |
float _x = float(x)/points_per_axis; | |
float _y = float(y)/points_per_axis; | |
result.add(MapToConcentricHemisphere(_x, _y, isCosine)); | |
} | |
} | |
return result; | |
} | |
void draw() { | |
background(125, 130, 120); | |
strokeWeight(2); | |
float radius = 280; | |
ArrayList<PVector> hemisphere = ConcentricHemisphere( | |
POINTS_PER_AXIS, COSINE_DISTRIBUTION, SKIP_BASE_RING); | |
noFill(); | |
stroke(255); | |
circle(width/2, height/2, radius); | |
for (int i = 0; i < hemisphere.size(); i++) { | |
float x = hemisphere.get(i).x; | |
float y = hemisphere.get(i).y; | |
float z = hemisphere.get(i).z; | |
// Generate some copypasta C... | |
if (i == 0) { | |
print( | |
"#define DOME_" + | |
(COSINE_DISTRIBUTION ? "COSINE_" : "UNIFORM_") + | |
(POINTS_PER_AXIS*POINTS_PER_AXIS) + | |
(SKIP_BASE_RING ? "X " : " ") + | |
"{\\\n"); | |
} | |
print("\t" + x + ", " + y + ", " + z); | |
if (i == hemisphere.size()-1) { | |
print(" };"); | |
} else { | |
print(",\\\n"); | |
} | |
float px = width/2 + radius*x; | |
float py = height/2 + radius*y; | |
fill( | |
floor( (x + 1.0)/2.0*255.0 + 0.5 ), | |
floor( (y + 1.0)/2.0*255.0 + 0.5 ), | |
floor( (z*0.5 + 0.5)*255 + 0.5 )); | |
// stroke(0); | |
noStroke(); | |
circle(px, py, 15); | |
fill(0, 0, 0); | |
text(i, px-7, py+6); | |
fill(255, 255, 255); | |
text(i, px-8, py+5); | |
} | |
} |
@jdolan
It's a Processing script, which is like an IDE for creative coding.
https://processing.org/download
Just download it, and paste the code into it when you run.
I had to edit it for the 4.0 version: you might need to fetch javafx with the builtin package manager, I added a comment above the import.
C code gets dumped to the console.
Thanks dude! Works mint! Hope you are well 😊
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@SpineyPete I'd love to try using this tool to bake lighting direction into the alpha channel of lightmaps, but I'm not sure how to run this. I see we have a 64 point version of this saved in
quemap
, but I'd love to use the 255 point version for maximum precision. Would you be able to share the C points array for DOME_COSINE_255X?