-
Notifications
You must be signed in to change notification settings - Fork 408
Add OSL hextiling implementations #2734
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jstone-lucasfilm
wants to merge
4
commits into
AcademySoftwareFoundation:main
Choose a base branch
from
jstone-lucasfilm:dev_hextiling_osl
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| // https://www.shadertoy.com/view/4djSRW | ||
| vector2 mx_hextile_hash(vector2 p) | ||
| { | ||
| vector pp3 = fmod(vector(p.x, p.y, p.x) * vector(0.1031, 0.1030, 0.0973), 1.0); | ||
| pp3 += dot(pp3, vector(pp3[1], pp3[2], pp3[0]) + 33.33); | ||
| return fmod(vector2(pp3[0] + pp3[1], pp3[0] + pp3[2]) * vector2(pp3[2], pp3[1]), 1.0); | ||
| } | ||
|
|
||
| // Christophe Schlick. "Fast Alternatives to Perlin's Bias and Gain Functions". | ||
| // In Graphics Gems IV, Morgan Kaufmann, 1994, pages 401-403. | ||
| // https://dept-info.labri.fr/~schlick/DOC/gem2.html | ||
| float mx_schlick_gain(float x, float r) | ||
| { | ||
| float rr = clamp(r, 0.001, 0.999); // to avoid glitch | ||
| float a = (1.0 / rr - 2.0) * (1.0 - 2.0 * x); | ||
| return (x < 0.5) ? x / (a + 1.0) : (a - x) / (a - 1.0); | ||
| } | ||
|
|
||
| struct HextileData | ||
| { | ||
| vector2 coords[3]; | ||
| vector weights; | ||
| vector rotations; | ||
| vector2 ddx[3]; | ||
| vector2 ddy[3]; | ||
| }; | ||
|
|
||
| // Helper function to compute blend weights with optional falloff | ||
| vector mx_hextile_compute_blend_weights(vector luminance_weights, vector tile_weights, float falloff) | ||
| { | ||
| vector w = luminance_weights * pow(tile_weights, 7.0); | ||
| w /= (w[0] + w[1] + w[2]); | ||
|
|
||
| if (falloff != 0.5) | ||
| { | ||
| w[0] = mx_schlick_gain(w[0], falloff); | ||
| w[1] = mx_schlick_gain(w[1], falloff); | ||
| w[2] = mx_schlick_gain(w[2], falloff); | ||
| w /= (w[0] + w[1] + w[2]); | ||
| } | ||
| return w; | ||
| } | ||
|
|
||
| // Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics | ||
| // Techniques (JCGT), vol. 11, no. 2, 77-94, 2022 | ||
| // http://jcgt.org/published/0011/03/05/ | ||
| HextileData mx_hextile_coord( | ||
| vector2 coord, | ||
| float rotation, | ||
| vector2 rotation_range, | ||
| float scale, | ||
| vector2 scale_range, | ||
| float offset, | ||
| vector2 offset_range) | ||
| { | ||
| float sqrt3_2 = sqrt(3.0) * 2.0; | ||
|
|
||
| // Scale coord to maintain the original fit | ||
| vector2 st = coord * sqrt3_2; | ||
|
|
||
| // Skew input space into simplex triangle grid | ||
| // (1, 0, -tan(30), 2*tan(30)) | ||
| vector2 st_skewed = vector2(st.x + st.y * -0.57735027, st.y * 1.15470054); | ||
|
|
||
| // Barycentric weights | ||
| vector2 st_frac = fmod(st_skewed, 1.0); | ||
| // Handle negative coordinates properly | ||
| if (st_frac.x < 0.0) st_frac.x += 1.0; | ||
| if (st_frac.y < 0.0) st_frac.y += 1.0; | ||
|
|
||
| vector temp = vector(st_frac.x, st_frac.y, 0.0); | ||
| temp[2] = 1.0 - temp[0] - temp[1]; | ||
|
|
||
| float s = step(0.0, -temp[2]); | ||
| float s2 = 2.0 * s - 1.0; | ||
|
|
||
| float w1 = -temp[2] * s2; | ||
| float w2 = s - temp[1] * s2; | ||
| float w3 = s - temp[0] * s2; | ||
|
|
||
| // Vertex IDs | ||
| int base_id_x = int(floor(st_skewed.x)); | ||
| int base_id_y = int(floor(st_skewed.y)); | ||
| int si = int(s); | ||
| int id1_x = base_id_x + si; | ||
| int id1_y = base_id_y + si; | ||
| int id2_x = base_id_x + si; | ||
| int id2_y = base_id_y + 1 - si; | ||
| int id3_x = base_id_x + 1 - si; | ||
| int id3_y = base_id_y + si; | ||
|
|
||
| // Vertex IDs as array for looping | ||
| int id_x[3] = { id1_x, id2_x, id3_x }; | ||
| int id_y[3] = { id1_y, id2_y, id3_y }; | ||
|
|
||
| // Tile centers, random values, and offsets | ||
| vector2 ctr[3], rand[3], offsets[3]; | ||
| vector2 seed_offset = vector2(0.12345, 0.12345); // To avoid some zeros | ||
| vector2 rr = vector2(radians(rotation_range.x), radians(rotation_range.y)); | ||
| vector rotations, scales; | ||
|
|
||
| for (int i = 0; i < 3; i++) | ||
| { | ||
| ctr[i] = vector2(float(id_x[i]) + float(id_y[i]) * 0.5, float(id_y[i]) / 1.15470054) / sqrt3_2; | ||
| rand[i] = mx_hextile_hash(vector2(float(id_x[i]), float(id_y[i])) + seed_offset); | ||
| rotations[i] = mix(rr.x, rr.y, rand[i].x * rotation); | ||
| scales[i] = mix(1.0, mix(scale_range.x, scale_range.y, rand[i].y), scale); | ||
| offsets[i] = vector2( | ||
| mix(offset_range.x, offset_range.y, rand[i].x * offset), | ||
| mix(offset_range.x, offset_range.y, rand[i].y * offset)); | ||
| } | ||
|
|
||
| vector sin_r = sin(rotations); | ||
| vector cos_r = cos(rotations); | ||
|
|
||
| HextileData tile_data; | ||
| tile_data.weights = vector(w1, w2, w3); | ||
| tile_data.rotations = rotations; | ||
|
|
||
| // Transform coordinates and derivatives for each tile | ||
| vector2 ddx_coord = vector2(Dx(coord.x), Dx(coord.y)); | ||
| vector2 ddy_coord = vector2(Dy(coord.x), Dy(coord.y)); | ||
|
|
||
| for (int i = 0; i < 3; i++) | ||
| { | ||
| vector2 d = coord - ctr[i]; | ||
| float c = cos_r[i]; | ||
| float s = sin_r[i]; | ||
| float sc = scales[i]; | ||
|
|
||
| tile_data.coords[i] = vector2( | ||
| (d.x * c - d.y * s) / sc + ctr[i].x + offsets[i].x, | ||
| (d.x * s + d.y * c) / sc + ctr[i].y + offsets[i].y); | ||
|
|
||
| tile_data.ddx[i] = vector2( | ||
| (ddx_coord.x * c - ddx_coord.y * s) / sc, | ||
| (ddx_coord.x * s + ddx_coord.y * c) / sc); | ||
|
|
||
| tile_data.ddy[i] = vector2( | ||
| (ddy_coord.x * c - ddy_coord.y * s) / sc, | ||
| (ddy_coord.x * s + ddy_coord.y * c) / sc); | ||
| } | ||
|
|
||
| return tile_data; | ||
| } | ||
|
|
||
| // Blend 3 normals by blending the gradients | ||
| // Morten S. Mikkelsen, Surface Gradient-Based Bump Mapping Framework, Journal of | ||
| // Computer Graphics Techniques (JCGT), vol. 9, no. 3, 60-90, 2020 | ||
| // http://jcgt.org/published/0009/03/04/ | ||
| vector mx_normals_to_gradient(vector N, vector Np) | ||
| { | ||
| float d = dot(N, Np); | ||
| vector g = (d * N - Np) / max(1e-8, abs(d)); | ||
| return g; | ||
| } | ||
|
|
||
| vector mx_gradient_blend_3_normals(vector N, vector N1, float N1_weight, vector N2, float N2_weight, vector N3, float N3_weight) | ||
| { | ||
| float w1 = clamp(N1_weight, 0.0, 1.0); | ||
| float w2 = clamp(N2_weight, 0.0, 1.0); | ||
| float w3 = clamp(N3_weight, 0.0, 1.0); | ||
|
|
||
| vector g1 = mx_normals_to_gradient(N, N1); | ||
| vector g2 = mx_normals_to_gradient(N, N2); | ||
| vector g3 = mx_normals_to_gradient(N, N3); | ||
|
|
||
| // Blend | ||
| vector gg = w1 * g1 + w2 * g2 + w3 * g3; | ||
|
|
||
| // Gradient to normal | ||
| return normalize(N - gg); | ||
| } | ||
|
|
||
| // Axis-angle rotation matrix | ||
| matrix mx_axis_rotation_matrix(vector axis, float angle) | ||
| { | ||
| float s = sin(angle); | ||
| float c = cos(angle); | ||
| float omc = 1.0 - c; | ||
| vector a = axis; | ||
|
|
||
| return matrix( | ||
| a[0]*a[0]*omc + c, a[0]*a[1]*omc - a[2]*s, a[0]*a[2]*omc + a[1]*s, 0.0, | ||
| a[1]*a[0]*omc + a[2]*s, a[1]*a[1]*omc + c, a[1]*a[2]*omc - a[0]*s, 0.0, | ||
| a[2]*a[0]*omc - a[1]*s, a[2]*a[1]*omc + a[0]*s, a[2]*a[2]*omc + c, 0.0, | ||
| 0.0, 0.0, 0.0, 1.0 | ||
| ); | ||
| } | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| #include "lib/$fileTransformUv" | ||
| #include "lib/mx_hextile.osl" | ||
|
|
||
| // Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics | ||
| // Techniques (JCGT), vol. 11, no. 2, 77-94, 2022 | ||
| // http://jcgt.org/published/0011/03/05/ | ||
| void mx_hextiledimage_color3( | ||
| textureresource file, | ||
| color default_value, | ||
| vector2 texcoord, | ||
| vector2 tiling, | ||
| float rotation, | ||
| vector2 rotationrange, | ||
| float scale, | ||
| vector2 scalerange, | ||
| float offset, | ||
| vector2 offsetrange, | ||
| float falloff, | ||
| float falloffcontrast, | ||
| color lumacoeffs, | ||
| output color result) | ||
| { | ||
| if (file.filename == "") | ||
| { | ||
| result = default_value; | ||
| return; | ||
| } | ||
|
|
||
| vector2 coord = mx_transform_uv(texcoord) * tiling; | ||
|
|
||
| HextileData tile_data = mx_hextile_coord(coord, rotation, rotationrange, scale, scalerange, offset, offsetrange); | ||
|
|
||
| // Sample textures with explicit derivatives | ||
| color c[3]; | ||
| vector cw; | ||
|
|
||
| for (int i = 0; i < 3; i++) | ||
| { | ||
| float filter_width = length(tile_data.ddx[i]) + length(tile_data.ddy[i]); | ||
| c[i] = texture(file.filename, tile_data.coords[i].x, tile_data.coords[i].y, | ||
| "swidth", filter_width, "twidth", filter_width, | ||
| "missingcolor", default_value, | ||
| "swrap", "periodic", "twrap", "periodic" | ||
| #if OSL_VERSION_MAJOR >= 1 && OSL_VERSION_MINOR >= 14 | ||
jstone-lucasfilm marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| , "colorspace", file.colorspace | ||
| #endif | ||
| ); | ||
| cw[i] = dot(vector(c[i]), vector(lumacoeffs)); | ||
| } | ||
|
|
||
| // Luminance-based blend weights | ||
| cw = mix(vector(1.0), cw, vector(falloffcontrast)); | ||
| vector w = mx_hextile_compute_blend_weights(cw, tile_data.weights, falloff); | ||
|
|
||
| // Blend colors | ||
| result = w[0] * c[0] + w[1] * c[1] + w[2] * c[2]; | ||
| } | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| #include "lib/$fileTransformUv" | ||
| #include "lib/mx_hextile.osl" | ||
|
|
||
| // Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics | ||
| // Techniques (JCGT), vol. 11, no. 2, 77-94, 2022 | ||
| // http://jcgt.org/published/0011/03/05/ | ||
| void mx_hextiledimage_color4( | ||
| textureresource file, | ||
| color4 default_value, | ||
| vector2 texcoord, | ||
| vector2 tiling, | ||
| float rotation, | ||
| vector2 rotationrange, | ||
| float scale, | ||
| vector2 scalerange, | ||
| float offset, | ||
| vector2 offsetrange, | ||
| float falloff, | ||
| float falloffcontrast, | ||
| color lumacoeffs, | ||
| output color4 result) | ||
| { | ||
| if (file.filename == "") | ||
| { | ||
| result = default_value; | ||
| return; | ||
| } | ||
|
|
||
| vector2 coord = mx_transform_uv(texcoord) * tiling; | ||
|
|
||
| HextileData tile_data = mx_hextile_coord(coord, rotation, rotationrange, scale, scalerange, offset, offsetrange); | ||
|
|
||
| // Sample textures with explicit derivatives | ||
| color c[3]; | ||
| float alpha[3]; | ||
| vector cw; | ||
|
|
||
| for (int i = 0; i < 3; i++) | ||
| { | ||
| float filter_width = length(tile_data.ddx[i]) + length(tile_data.ddy[i]); | ||
| c[i] = texture(file.filename, tile_data.coords[i].x, tile_data.coords[i].y, | ||
| "alpha", alpha[i], | ||
| "swidth", filter_width, "twidth", filter_width, | ||
| "missingcolor", default_value.rgb, "missingalpha", default_value.a, | ||
| "swrap", "periodic", "twrap", "periodic" | ||
| #if OSL_VERSION_MAJOR >= 1 && OSL_VERSION_MINOR >= 14 | ||
| , "colorspace", file.colorspace | ||
| #endif | ||
| ); | ||
| cw[i] = dot(vector(c[i]), vector(lumacoeffs)); | ||
| } | ||
|
|
||
| // Luminance-based blend weights | ||
| cw = mix(vector(1.0), cw, vector(falloffcontrast)); | ||
| vector w = mx_hextile_compute_blend_weights(cw, tile_data.weights, falloff); | ||
|
|
||
| // Alpha (average with optional falloff adjustment) | ||
| float a = (alpha[0] + alpha[1] + alpha[2]) / 3.0; | ||
| if (falloff != 0.5) | ||
| { | ||
| a = mx_schlick_gain(a, falloff); | ||
| } | ||
|
|
||
| // Blend colors | ||
| result.rgb = w[0] * c[0] + w[1] * c[1] + w[2] * c[2]; | ||
| result.a = a; | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| #include "lib/$fileTransformUv" | ||
| #include "lib/mx_hextile.osl" | ||
|
|
||
| // Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics | ||
| // Techniques (JCGT), vol. 11, no. 2, 77-94, 2022 | ||
| // http://jcgt.org/published/0011/03/05/ | ||
| void mx_hextilednormalmap_vector3( | ||
| textureresource file, | ||
| vector default_value, | ||
| vector2 texcoord, | ||
| vector2 tiling, | ||
| float rotation, | ||
| vector2 rotationrange, | ||
| float scale, | ||
| vector2 scalerange, | ||
| float offset, | ||
| vector2 offsetrange, | ||
| float falloff, | ||
| float strength, | ||
| int flip_g, | ||
| vector N, | ||
| vector T, | ||
| vector B, | ||
| output vector result) | ||
| { | ||
| if (file.filename == "") | ||
| { | ||
| result = N; | ||
| return; | ||
| } | ||
|
|
||
| vector2 coord = mx_transform_uv(texcoord) * tiling; | ||
|
|
||
| HextileData tile_data = mx_hextile_coord(coord, rotation, rotationrange, scale, scalerange, offset, offsetrange); | ||
|
|
||
| // Process each tile: sample, decode, transform, and compute normals | ||
| vector tile_normals[3]; | ||
|
|
||
| for (int i = 0; i < 3; i++) | ||
| { | ||
| // Sample normal map with explicit derivatives | ||
| float filter_width = length(tile_data.ddx[i]) + length(tile_data.ddy[i]); | ||
| color nm_raw = texture(file.filename, tile_data.coords[i].x, tile_data.coords[i].y, | ||
| "swidth", filter_width, "twidth", filter_width, | ||
| "missingcolor", color(default_value), | ||
| "swrap", "periodic", "twrap", "periodic"); | ||
|
|
||
| // Convert to vector and decode normal map | ||
| vector nm = vector(nm_raw); | ||
| if (flip_g) | ||
| nm[1] = 1.0 - nm[1]; | ||
| nm = 2.0 * nm - 1.0; | ||
|
|
||
| // Rotate tangent frame for this tile | ||
| matrix tangent_rot_mat = mx_axis_rotation_matrix(N, -tile_data.rotations[i]); | ||
| vector Ti = transform(tangent_rot_mat, T) * strength; | ||
| vector Bi = transform(tangent_rot_mat, B) * strength; | ||
|
|
||
| // The OSL backend uses dPdu and dPdv for tangents and bitangents, but these vectors are not | ||
| // guaranteed to be orthonormal. Orthogonalize the tangent frame using Gram-Schmidt. | ||
| vector Tn = normalize(Ti - dot(Ti, N) * N); | ||
| vector Bn = normalize(Bi - dot(Bi, N) * N - dot(Bi, Tn) * Tn); | ||
|
|
||
| tile_normals[i] = normalize(Tn * nm[0] + Bn * nm[1] + N * nm[2]); | ||
| } | ||
|
|
||
| // Blend weights and normals using gradient blending | ||
| vector w = mx_hextile_compute_blend_weights(vector(1.0), tile_data.weights, falloff); | ||
| result = mx_gradient_blend_3_normals(N, tile_normals[0], w[0], tile_normals[1], w[1], tile_normals[2], w[2]); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.