Skip to content

Instantly share code, notes, and snippets.

@Denchyaknow
Created March 8, 2025 00:57
Show Gist options
  • Save Denchyaknow/e2dec246b7f540daf10a6acb6051c6bd to your computer and use it in GitHub Desktop.
Save Denchyaknow/e2dec246b7f540daf10a6acb6051c6bd to your computer and use it in GitHub Desktop.
HowTo_CalculateTilingAndOffset
//This logic is good for when you have a childed Quad whos UV coodinates need to match another parent Quads UV Coods.
public class UVMapper
{
private float scaleFactorX = 1.0f;
private float scaleFactorY = 1.0f;
private float quad_aspect = 1f;//LocalScale Aspect of Quads LocalScale X/Y
private float frame_aspect = 1f;//Pixel Aspect of ImageFrames Width/Height
private float pixelHeight = 1f;
private float pixelWidth = 1f;
Vector3 quad_scale = Vector3.one;
const float MIN_SCALE = 0.001f;
const float PAN_VALUE = 0.5f;//For Tiling and Offsetting UV Coordinates
public void UpdateQuadUVs(IRenderSurface surface)
{
// Get the Transform of the IRenderSurface (ImageFrame.Quad)
Vector3 quadWorldScale = surface.Scale;
quadWorldScale.x = Mathf.Max(quadWorldScale.x, MIN_SCALE);
quadWorldScale.y = Mathf.Max(quadWorldScale.y, MIN_SCALE);
float frameWidth = Mathf.Max(surface.PixelWidth, 1f);
float frameHeight = Mathf.Max(surface.PixelHeight, 1f);
if (!adjustUVs)
{
//This bit works but not what we wanted, it scales the child quad relative to the parent quad
frame_aspect = frameWidth >= frameHeight ? frameWidth / frameHeight: frameHeight / frameWidth;
scaleFactorX = Mathf.Abs(quadWorldScale.x);
scaleFactorY = Mathf.Abs(quadWorldScale.y);
if (scaleFactorX > scaleFactorY)
{
quad_aspect = scaleFactorX / scaleFactorY;
quad_scale.x = 1f;
quad_scale.y = quad_aspect;
}
else if (scaleFactorY > scaleFactorX)
{
quad_aspect = scaleFactorY / scaleFactorX;
quad_scale.x = quad_aspect;
quad_scale.y = 1f;
}
transform.localScale = quad_scale;
}
}
void DrawLine(Color brushColor, float brushSize, Vector2 startUnscaled, Vector2 endUnscaled)
{
Vector2 start_pix = Vector2.zero;
Vector2 end_pix = Vector2.zero;
Vector2 start = Vector2.one;
Vector2 end = Vector2.one;
if (!adjustUVs)
{
// Convert to pixel coordinates for the shader
// This is the Original Drawing Logic
start_pix = startUnscaled * TEXTURE_SIZE;
end_pix = endUnscaled * TEXTURE_SIZE;
}
else
{
//Apply the tiling and offset to the UV coordinates just like the shader does
start = ApplyTilingAndOffset(startUnscaled);
end = ApplyTilingAndOffset(endUnscaled);
//Convert to UVs to pixel coordinates for the shader
start_pix = start * TEXTURE_SIZE;
end_pix = end * TEXTURE_SIZE;
}
if (verbose)
{
Debug.Log($"UVAdjusted: start({start.x}, {start.y}) end({end.x}, {end.y})");
Debug.Log($"UVOriginal: startUnscaled({startUnscaled.x}, {startUnscaled.y}), endUnscaled({endUnscaled.x}, {endUnscaled.y})");
}
shader_instance.SetVector(start_position_id, start_pix);
shader_instance.SetVector(end_position_id, end_pix);
shader_instance.SetFloat(stroke_smoothing_id, strokeSmoothingInterval);
shader_instance.SetFloat(brush_size_id, brushSize);
shader_instance.SetVector(brush_color_id, brushColor);
// Set all required textures for kernel_update
SetShaderTexturesForKernelUpdate();
shader_instance.Dispatch(kernel_update, thread_groups_x, thread_groups_y, 1);
SwapBuffers(current_page);
}
Vector2 CalculateTiling(Vector3 localScale)
{
Vector2 tiling = Vector2.one;
var widthAspect = localScale.x / localScale.y;
var heightAspect = localScale.y / localScale.x;
if(widthAspect > heightAspect)
{
tiling.x = widthAspect;
}
else
{
tiling.y = heightAspect;
}
return tiling;
}
Vector2 CalculateOffset(Vector3 localScale, float panning)
{
Vector2 offset = Vector2.zero;
var widthAspect = localScale.x / localScale.y;
var heightAspect = localScale.y / localScale.x;
if (widthAspect > heightAspect)
{
offset.y = Mathf.Lerp(0f, (1f - heightAspect), panning);
}
else
{
offset.x = Mathf.Lerp(0f, (1f - widthAspect), panning);
}
return offset;
}
/// <summary>
/// Applies tiling and offset to UV coordinates
/// </summary>
/// <param name="uv">Original UV coordinates</param>
/// <param name="tiling">Tiling factor (x, y)</param>
/// <param name="offset">Offset value (x, y)</param>
/// <returns>Transformed UV coordinates</returns>
Vector2 ApplyTilingAndOffset(Vector2 uv)
{
Vector2 tiling = Vector2.one;
Vector2 offset = Vector2.zero;
//The Scale of my parent Quad, my scale is always 1,1,1
var widthAspect = quad_scale.x / quad_scale.y;
var heightAspect = quad_scale.y / quad_scale.x;
if (widthAspect > heightAspect)
{
tiling.y = heightAspect;
offset.y = Mathf.Lerp(0f, (1f - heightAspect), PAN_VALUE);
}
else
{
tiling.x = widthAspect;
offset.x = Mathf.Lerp(0f, (1f - widthAspect), PAN_VALUE);
}
// First apply tiling (scale)
Vector2 scaledUV = new Vector2(uv.x * tiling.x, uv.y * tiling.y);
// Then apply offset (translation)
Vector2 finalUV = new Vector2(scaledUV.x + offset.x, scaledUV.y + offset.y);
return finalUV;
}
/// <summary>
/// Extended version with additional parameters visible in the shader graph
/// </summary>
/// <param name="uv">Original UV coordinates</param>
/// <param name="tiling">Tiling factor (x, y)</param>
/// <param name="offset">Offset value (x, y)</param>
/// <param name="sample">Optional sample texture coordinates for more complex effects</param>
/// <param name="shapeTexture">Optional shape texture influence</param>
/// <returns>Transformed UV coordinates</returns>
Vector2 ApplyAdvancedTilingAndOffset(Vector2 uv, Vector2 tiling, Vector2 offset, Vector2 sample = default, float shapeInfluence = 0f)
{
// Basic tiling and offset
Vector2 tiledUV = new Vector2(uv.x * tiling.x, uv.y * tiling.y);
// Apply sample influence if provided
if (sample != default)
{
tiledUV += new Vector2(sample.x * shapeInfluence, sample.y * shapeInfluence);
}
// Apply offset
Vector2 finalUV = new Vector2(tiledUV.x + offset.x, tiledUV.y + offset.y);
return finalUV;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment