Skip to content

Instantly share code, notes, and snippets.

@staggartcreations
Last active February 28, 2022 18:33
Show Gist options
  • Save staggartcreations/dfe566c20f56856cc066e933c53d8966 to your computer and use it in GitHub Desktop.
Save staggartcreations/dfe566c20f56856cc066e933c53d8966 to your computer and use it in GitHub Desktop.
World-space texel sampling in Unity
using System.Collections;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
[ExecuteInEditMode]
public class WorldSpaceTexel : MonoBehaviour
{
public class WorldSpaceTexelData
{
public Vector3 position;
public Color color;
}
public Texture2D texture;
private List<WorldSpaceTexelData> texelData = new List<WorldSpaceTexelData>();
[Range(8, 128)]
public int samples = 32;
[Header("Gizmos (slow!)")]
public bool showSpheres = false;
public bool showLines = true;
public Bounds worldBounds;
public void OnEnable()
{
//World bounds, hardcoded for a centered 1x1km test plane
worldBounds = new Bounds(Vector3.zero, new Vector3(1000f, 0f, 1000f));
RefreshTexelData();
}
public void RefreshTexelData()
{
if (texture == null)
{
texelData.Clear();
return;
}
//Can't use more samples than there are texels
samples = Mathf.Clamp(samples, 0, texture.width);
texelData.Clear();
int i = 0;
#if UNITY_EDITOR
EditorUtility.DisplayProgressBar(this.name, "Sampling texel", 0);
#endif
//2D loop
for (int x = 0; x <= samples; x++)
{
//World-position for sample point on X-axis
float wPosX = x * (worldBounds.size.x / samples);
for (int z = 0; z <= samples; z++)
{
//World-position for sample point on Z-axis
float wPosZ = z * (worldBounds.size.z / samples);
//Create texel data
WorldSpaceTexelData td = new WorldSpaceTexelData()
{
position = new Vector3(worldBounds.min.x + wPosX, 0f,worldBounds.min.z + wPosZ),
color = texture.GetPixel(x * (texture.width / samples), z * (texture.height / samples))
};
texelData.Add(td);
i++;
}
i++;
}
#if UNITY_EDITOR
EditorUtility.ClearProgressBar();
#endif
Debug.Log("Created " + texelData.Count + " texel samples");
}
//Get TexelData for closest matching position
public WorldSpaceTexelData GetCacheData(Vector3 pos)
{
if (texelData.Count == 0)
{
return new WorldSpaceTexelData();
}
float distance = 999;
int closestIndex = 0;
for (int i = 0; i < texelData.Count; i++)
{
float d = Vector3.Distance(texelData[i].position, pos);
//Find position with lowest distance
if (d < distance)
{
distance = d;
closestIndex = i;
}
}
return texelData[closestIndex];
}
//Transform world-space position to position relative to bounds and texture dimensions
public Vector2 WorldToTexelPosition(Vector3 pos)
{
return new Vector2(
((pos.x - worldBounds.min.x) / worldBounds.size.x) * texture.width,
((pos.z - worldBounds.min.z) / worldBounds.size.z) * texture.height
);
}
//Sample texel color at world-space position. Slow but accurate
public Color GetTexelAtPosition(Vector3 wsPos)
{
Vector2 texelCoordinates = WorldToTexelPosition(wsPos);
return texture.GetPixel((int)texelCoordinates.x, (int)texelCoordinates.y);
}
private void OnDrawGizmos()
{
if (texelData == null) return;
Gizmos.DrawWireCube(worldBounds.center, worldBounds.size);
foreach (WorldSpaceTexelData td in texelData)
{
Gizmos.color = new Color(td.color.r, td.color.g, td.color.b, 1f);
if (showSpheres) Gizmos.DrawSphere(td.position, (250f / samples));
//Texel color as XZ direction
if (showLines) Gizmos.DrawLine(td.position, td.position + (new Vector3(td.color.r, 0f, td.color.g) * (1000f / samples)));
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
//Test class for sampling texel data
public class WorldSpaceTexelSampler : MonoBehaviour
{
public WorldSpaceTexel data;
public bool useCachedData = true;
[Header("Output")]
public Color texel;
private MeshRenderer r;
private void OnEnable()
{
r = this.GetComponent<MeshRenderer>();
if (!data) data = GameObject.FindObjectOfType<WorldSpaceTexel>();
Sample();
}
void Update()
{
Sample();
}
void Sample()
{
if (!data) return;
if (useCachedData)
{
texel = data.GetCacheData(this.transform.position).color;
}
//Realtime fetching using GetPixel, slow!
else
{
texel = data.GetTexelAtPosition(this.transform.position);
}
r.sharedMaterial.color = texel;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment