Skip to content

Commit

Permalink
Add colors (#7)
Browse files Browse the repository at this point in the history
* rewrite

* fixed colors/shell clipping. added more problems.

* fix lighting

* fixed
  • Loading branch information
andyoneal authored Feb 6, 2024
1 parent a198d0b commit b8dacad
Show file tree
Hide file tree
Showing 12 changed files with 892 additions and 601 deletions.
20 changes: 8 additions & 12 deletions InstDysonShellLayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ public struct ShellData
public int polygonIndex;
public Vector3 center;
public int protoId;
public int clockwise;
}
public static int SHELLDATA_STRIDE = 36;
public static int SHELLDATA_STRIDE = 40;

public int layerId;

Expand Down Expand Up @@ -153,17 +154,6 @@ public int AddPolygonData(List<VectorLF3> polygon, VectorLF3[] polyn, bool clock
polygonPool[polygonCursor + i].normal = polyn[i];
}

if (!clockwise)
{
Array.Reverse(polygonPool, polygonCursor, polygon.Count);
for (int i = 0; i < polygon.Count; i++)
{
Vector3 vector = polygonPool[polygonCursor + i].pos;
Vector3 vector2 = polygonPool[polygonCursor + (i + 1) % polygon.Count].pos;
polygonPool[polygonCursor + i].normal = VectorLF3.Cross(vector, vector2).normalized;
}
}

polygonBufferIsDirty = true;

var polyIndex = polygonCursor;
Expand Down Expand Up @@ -297,5 +287,11 @@ public void UpdateState(int shellId, uint shellState)
shellPool[shellId].state = shellState;
shellBufferIsDirty = true;
}

public void UpdateColor(int shellId, Color32 shellColor)
{
shellPool[shellId].color = (shellColor.a << 24) | (shellColor.b << 16) | (shellColor.g << 8) | shellColor.r;
shellBufferIsDirty = true;
}
}
}
40 changes: 37 additions & 3 deletions Patch_DysonShell.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using HarmonyLib;
using System.Collections.Generic;
using System.Reflection.Emit;
using HarmonyLib;

namespace SphereOpt
{
Expand Down Expand Up @@ -31,7 +33,7 @@ private static void DysonShell_GenerateModelObjects(DysonShell __instance)
shellIndex = shellIndex,
nodeIndex = (int)__instance.uvs[i].x,
vertFillOrder = __instance.uvs[i].y,
closestPolygon = __instance.clockwise ? (int)__instance.uv2s[i].y : polygon.Count - (int)__instance.uv2s[i].y - 1,
closestPolygon = (int)__instance.uv2s[i].y,
axialCoords_xy =
(uint)(((__instance.vkeys[i] >> 16) - 10000) & 0x0000ffff | ((__instance.vkeys[i] & 0xFFFF) - 10000) << 16)
};
Expand All @@ -51,7 +53,8 @@ private static void DysonShell_GenerateModelObjects(DysonShell __instance)
polygonIndex = polygonIndex,
polyCount = polygon.Count,
center = __instance.center,
protoId = __instance.protoId
protoId = __instance.protoId,
clockwise = __instance.clockwise ? 1 : -1
};
instShellLayer.AddShellData(shellIndex, shellData);
instShellLayer.shellBufferIsDirty = true;
Expand Down Expand Up @@ -117,5 +120,36 @@ private static void DysonSphereLayer_Free(DysonSphereLayer __instance)
var instDysonShellRenderer = SphereOpt.getInstDysonShellRendererForSphere(__instance.dysonSphere);
instDysonShellRenderer?.RemoveLayer(__instance.id);
}

[HarmonyPatch(typeof(UIDEToolbox), "OnColorChange")]
[HarmonyPostfix]
private static void UIDEToolbox_OnColorChange(UIDEToolbox __instance)
{
foreach (DysonShell selectedShell in __instance.editor.selection.selectedShells)
{
SphereOpt.UpdateColor(selectedShell);
}
}

[HarmonyTranspiler]
[HarmonyPatch(typeof(UIDysonBrush_Paint), "_OnUpdate")]
static IEnumerable<CodeInstruction> UIDysonBrush_Paint__OnUpdate(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
CodeMatcher matcher = new CodeMatcher(instructions, generator);

matcher.MatchForward(true,
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonShell), nameof(DysonShell.color)))
).Advance(-12);

var shellVarOpCode = matcher.Opcode;
var shellVarOperand = matcher.Operand;

matcher.Advance(13).InsertAndAdvance(
new CodeInstruction(shellVarOpCode, shellVarOperand),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(SphereOpt), nameof(SphereOpt.UpdateColor)))
);

return matcher.InstructionEnumeration();
}
}
}
7 changes: 7 additions & 0 deletions SphereOpt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,12 @@ public static void RemoveRenderer(DysonSphere ds)
{
instRenderers.Remove(ds.starData.id);
}

public static void UpdateColor(DysonShell shell)
{
var dsRenderer = getInstDysonShellRendererForSphere(shell.dysonSphere);
var layer = dsRenderer.getInstShellLayer(shell.layerId);
layer.UpdateColor(shell.id, shell.color);
}
}
}
215 changes: 170 additions & 45 deletions shaders/CGIncludes/DSPCommon.cginc
Original file line number Diff line number Diff line change
@@ -1,52 +1,71 @@
#include "UnityCG.cginc"

#define INV_TEN_PI 0.0318309888

UNITY_DECLARE_TEXCUBE(_Global_PGI);
float _PGI_Gray;

struct GPUOBJECT
{
uint objId;
float3 pos;
float4 rot;
uint objId;
float3 pos;
float4 rot;
};

struct AnimData
{
float time;
float prepare_length;
float working_length;
uint state;
float power;
float time;
float prepare_length;
float working_length;
uint state;
float power;
};

inline float3 rotate_vector_fast(float3 v, float4 r){
float3 rotate_vector_fast(float3 v, float4 r){
return v + cross(2.0 * r.xyz, cross(r.xyz, v) + r.w * v);
}

inline float SchlickFresnel_Approx(float F0, float vDotH)
float3 GammaToLinear_Approx(float c)
{
return pow((c + 0.055)/1.055, 2.4);
}

float3 GammaToLinear_Approx(float3 c)
{
return pow((c + 0.055)/1.055, 2.4);
}

float SchlickFresnel_Approx(float F0, float vDotH)
{
return F0 + (1 - F0) * exp2((-5.55473 * vDotH - 6.98316) * vDotH);
}

inline float3 calculateLightFromHeadlamp(float4 headlampPos, float3 upDir, float3 lightDir, float3 worldNormal) {
float isHeadlampOn = headlampPos.w >= 0.5 ? 1.0 : 0.0;
if (headlampPos.w < 0.5) return float3(0, 0, 0);
float3 calculateLightFromHeadlamp(float4 headlampPos, float3 upDir, float3 lightDir, float3 worldNormal, float brightness) {
bool isHeadlampOn = headlampPos.w >= 0.5;
if (!isHeadlampOn) return float3(0, 0, 0);

float distanceFromHeadlamp = length(headlampPos) - 5.0;
float headlampVisibility = saturate(distanceFromHeadlamp);
float daylightDimFactor = saturate(dot(-upDir, lightDir) * 5.0);

float3 directionToPlayer = headlampPos - upDir * distanceFromHeadlamp;
float distObjToPlayer = length(directionToPlayer);
directionToPlayer /= distObjToPlayer;

float falloff = pow(max((20.0 - distObjToPlayer) * 0.05, 0), 2);
float3 lightColor = float3(1.3, 1.1, 0.6);

float lightIntensity = headlampVisibility * daylightDimFactor * falloff * saturate(dot(directionToPlayer, worldNormal));
float3 computedLight = lightIntensity * lightColor;
float lightIntensity = falloff * saturate(dot(directionToPlayer, worldNormal));
lightIntensity = distObjToPlayer < 0.001 ? 1 : lightIntensity;
lightIntensity = lightIntensity * daylightDimFactor * headlampVisibility;

float3 lightColor = float3(1.3, 1.1, 0.6) * brightness;
return lightColor * lightIntensity;
}

return distObjToPlayer < 0.001 ? daylightDimFactor * lightColor : computedLight;
float3 calculateLightFromHeadlamp(float4 headlampPos, float3 upDir, float3 lightDir, float3 worldNormal) {
return calculateLightFromHeadlamp(headlampPos, upDir, lightDir, worldNormal, 1.0);
}

inline float distributionGGX(float roughness, float nDotH) {
float distributionGGX(float roughness, float nDotH) {
float a = roughness; //NDF formula says `a` should be roughness^2
//"We also adopted Disney’s reparameterization of α = Roughness2."
//but a = Roughness here
Expand All @@ -55,7 +74,7 @@ inline float distributionGGX(float roughness, float nDotH) {
//missing (1/PI) *
}

inline float geometrySchlickGGX(float roughness, float nDotV, float nDotL) {
float geometrySchlickGGX(float roughness, float nDotV, float nDotL) {
float k = pow(roughness * roughness + 1.0, 2) * 0.125; //r2.w does "roughness" mean perceptualroughness^2 or ^4?
//"We also chose to use Disney’s modification to reduce “hotness” by remapping roughness using (Roughness+1)/2 before squaring."
//but this is doing (Roughness^2+1)/2 before squaring
Expand All @@ -65,43 +84,149 @@ inline float geometrySchlickGGX(float roughness, float nDotV, float nDotL) {
//missing (nDotL * nDotV) *
}

inline float GGX(float roughness, float metallic, float nDotH, float nDotV, float nDotL, float vDotH) {
float D = distributionGGX(roughness, nDotH);
float GGX(float roughness, float metallic, float nDotH, float nDotV, float nDotL, float vDotH) {

float D = distributionGGX(roughness, nDotH);
float G = geometrySchlickGGX(roughness, nDotV, nDotL); //r1.x
float F = SchlickFresnel_Approx(metallic, vDotH);

return (D * F * G) / 4.0;
//should be (4.0 * nDotV * nDotL)
}

inline float3 calculateBinormal(float4 tangent, float3 normal ) {
#if defined(_ENABLE_VFINST)

int _VertexSize;
uint _VertexCount;
uint _FrameCount;
StructuredBuffer<float> _VertaBuffer;

void animateWithVerta(uint vertexID, float time, float prepare_length, float working_length, inout float3 pos, inout float3 normal, inout float3 tangent) {
float frameCount = prepare_length > 0 ? _FrameCount - 1 : _FrameCount; //r0.w
bool skipVerta = frameCount <= 0 || (_VertexSize != 9 && _VertexSize != 6 && _VertexSize != 3) || _VertexCount <= 0 || working_length <= 0; //r0.x
if (!skipVerta) {
float prepareTime = time >= prepare_length && prepare_length > 0 ? 1.0 : 0; //r0.x
prepareTime = frac(time / (prepare_length + working_length)) * (frameCount - 1) + prepareTime;
prepareTime = frameCount - 1 <= 0 ? 0 : prepareTime; //r0.x
uint prepareTimeSec = (uint)prepareTime; //r0.z
float prepareTimeFrac = frac(prepareTime); //r0.x
int frameStride = _VertexSize * _VertexCount; //r0.w
int offset = vertexID * _VertexSize; //r1.x
uint frameIdx = mad(frameStride, prepareTimeSec, offset); //r3.y
uint nextFrameIdx = mad(frameStride, prepareTimeSec + 1, offset); //r0.z

if (_VertexSize == 3) {
pos.x = lerp(_VertaBuffer[frameIdx], _VertaBuffer[nextFrameIdx], prepareTimeFrac);
pos.y = lerp(_VertaBuffer[frameIdx + 1], _VertaBuffer[nextFrameIdx + 1], prepareTimeFrac);
pos.z = lerp(_VertaBuffer[frameIdx + 2], _VertaBuffer[nextFrameIdx + 2], prepareTimeFrac);
} else {
if (_VertexSize == 6) {
pos.x = lerp(_VertaBuffer[frameIdx], _VertaBuffer[nextFrameIdx], prepareTimeFrac);
pos.y = lerp(_VertaBuffer[frameIdx + 1], _VertaBuffer[nextFrameIdx + 1], prepareTimeFrac);
pos.z = lerp(_VertaBuffer[frameIdx + 2], _VertaBuffer[nextFrameIdx + 2], prepareTimeFrac);
normal.x = lerp(_VertaBuffer[frameIdx + 3], _VertaBuffer[nextFrameIdx + 3], prepareTimeFrac);
normal.y = lerp(_VertaBuffer[frameIdx + 4], _VertaBuffer[nextFrameIdx + 4], prepareTimeFrac);
normal.z = lerp(_VertaBuffer[frameIdx + 5], _VertaBuffer[nextFrameIdx + 5], prepareTimeFrac);
} else {
if (_VertexSize == 9) {
pos.x = lerp(_VertaBuffer[frameIdx], _VertaBuffer[nextFrameIdx], prepareTimeFrac);
pos.y = lerp(_VertaBuffer[frameIdx + 1], _VertaBuffer[nextFrameIdx + 1], prepareTimeFrac);
pos.z = lerp(_VertaBuffer[frameIdx + 2], _VertaBuffer[nextFrameIdx + 2], prepareTimeFrac);
normal.x = lerp(_VertaBuffer[frameIdx + 3], _VertaBuffer[nextFrameIdx + 3], prepareTimeFrac);
normal.y = lerp(_VertaBuffer[frameIdx + 4], _VertaBuffer[nextFrameIdx + 4], prepareTimeFrac);
normal.z = lerp(_VertaBuffer[frameIdx + 5], _VertaBuffer[nextFrameIdx + 5], prepareTimeFrac);
tangent.x = lerp(_VertaBuffer[frameIdx + 6], _VertaBuffer[nextFrameIdx + 6], prepareTimeFrac);
tangent.y = lerp(_VertaBuffer[frameIdx + 7], _VertaBuffer[nextFrameIdx + 7], prepareTimeFrac);
tangent.z = lerp(_VertaBuffer[frameIdx + 8], _VertaBuffer[nextFrameIdx + 8], prepareTimeFrac);
}
}
}
}
}

#else

void animateWithVerta(uint vertexID, float time, float prepare_length, float working_length, inout float3 pos, inout float3 normal, inout float3 tangent) {
return;
}


#endif

float3 calculateBinormal(float4 tangent, float3 normal ) {
float sign = tangent.w * unity_WorldTransformParams.w;
float3 binormal = cross(normal.xyz, tangent.xyz) * sign;
return binormal;
}

UNITY_DECLARE_TEXCUBE(_Global_PGI);


/* What image is reflected in metallic surfaces and how reflective is it? */
inline float3 reflection(float perceptualRoughness, float3 metallicLow, float3 upDir, float3 viewDir, float3 worldNormal, out float reflectivity) {
float upDirMagSqr = dot(upDir, upDir);
bool validUpDirY = upDirMagSqr > 0.01 && upDir.y < 0.9999;
float3 xaxis = validUpDirY ? normalize(cross(upDir.zxy, float3(0, 0, 1))) : float3(0, 1, 0);
bool validUpDirXY = dot(xaxis, xaxis) > 0.01 && upDirMagSqr > 0.01;
float3 zaxis = validUpDirXY ? normalize(cross(xaxis.yzx, upDir)) : float3(0, 0, 1);
float3 reflection(float perceptualRoughness, float3 metallic, float3 upDir, float3 viewDir, float3 worldNormal, out float reflectivity) {
bool validUpDir = dot(upDir, upDir) > 0.01;
bool upDirNotStraightUp = upDir.y < 0.9999;

float3 rightDir = normalize(cross(upDir, float3(0, 1, 0)));
rightDir = validUpDir && upDirNotStraightUp ? rightDir : float3(1, 0, 0);

bool validRightDir = dot(rightDir, rightDir) > 0.01;

float3 fwdDir = normalize(cross(rightDir, upDir));
fwdDir = validUpDir && validRightDir ? fwdDir : float3(0, 0, 1);

float3 reflectDir = reflect(-viewDir, worldNormal);

float3 worldReflect = reflect(-viewDir, worldNormal);
float3 reflectDir;
reflectDir.x = dot(worldReflect.zxy, -xaxis);
reflectDir.y = dot(worldReflect, upDir);
reflectDir.z = dot(worldReflect, -zaxis);
float3 worldReflect;
worldReflect.x = dot(reflectDir, -rightDir);
worldReflect.y = dot(reflectDir, upDir);
worldReflect.z = dot(reflectDir, -fwdDir);

float reflectLOD = 10.0 * pow(perceptualRoughness, 0.4);
float3 g_PGI = UNITY_SAMPLE_TEXCUBE_LOD(_Global_PGI, reflectDir, reflectLOD);
float lod = 10.0 * pow(perceptualRoughness, 0.4);
float3 reflectColor = UNITY_SAMPLE_TEXCUBE_LOD(_Global_PGI, worldReflect, lod).xyz;
float greyscaleReflectColor = dot(reflectColor, float3(0.29, 0.58, 0.13));
reflectColor = lerp(reflectColor, greyscaleReflectColor.xxx, _PGI_Gray);

float scaled_metallicLow = metallicLow * 0.7 + 0.3;
reflectivity = scaled_metallicLow * (1.0 - perceptualRoughness);
float scaledMetallic = metallic * 0.7 + 0.3;
float smoothness = 1.0 - perceptualRoughness;
reflectivity = scaledMetallic * smoothness;

return reflectColor * reflectivity;
}

float3 calculateSunlightColor(float3 sunlightColor, float upDotL, float3 sunsetColor0, float3 sunsetColor1, float3 sunsetColor2, float3 lightColorScreen) {
sunlightColor = lerp(sunlightColor, float3(1,1,1), lightColorScreen);

float3 sunsetColor = float3(1,1,1);
if (upDotL <= 1) {
sunsetColor0 = lerp(sunsetColor0, float3(1,1,1), lightColorScreen);
sunsetColor1 = lerp(sunsetColor1, float3(1,1,1), lightColorScreen) * float3(1.25, 1.25, 1.25);
sunsetColor2 = lerp(sunsetColor2, float3(1,1,1), lightColorScreen) * float3( 1.5, 1.5, 1.5);

float3 blendDawn = lerp(float3(0,0,0), sunsetColor2, saturate( 5 * (upDotL + 0.3)));
float3 blendSunrise = lerp(sunsetColor2, sunsetColor1, saturate( 5 * (upDotL + 0.1)));
float3 blendMorning = lerp(sunsetColor1, sunsetColor0, saturate(10 * (upDotL - 0.1)));
float3 blendDay = lerp(sunsetColor0, float3(1,1,1), saturate( 5 * (upDotL - 0.2)));

sunsetColor = upDotL > -0.1 ? blendSunrise : blendDawn;
sunsetColor = upDotL > 0.1 ? blendMorning : sunsetColor.xyz;
sunsetColor = upDotL > 0.2 ? blendDay : sunsetColor.xyz;
}

return sunsetColor.xyz * sunlightColor.xyz;
}

float3 calculateSunlightColor(float3 sunlightColor, float upDotL, float3 sunsetColor0, float3 sunsetColor1, float3 sunsetColor2) {
return calculateSunlightColor(sunlightColor, upDotL, sunsetColor0, sunsetColor1, sunsetColor2, float3(0,0,0));
}

float3 calculateAmbientColor(float3 upDir, float3 lightDir, float3 ambientColor0, float3 ambientColor1, float3 ambientColor2) {
//UpdotL: position of star in the sky, relative to the object.
//1 is noon
//0 is sunrise/sunset
//-1 is midnight
float UpdotL = dot(upDir, lightDir);

return g_PGI * reflectivity;
//starting when the star is below the horizon, lerp from ambient2 to ambient1 to ambient0 at noon, then back down again
float3 ambientTwilight = lerp(ambientColor2, ambientColor1, saturate(UpdotL * 3.0 + 1)); //-33% to 0%
float3 ambientLowSun = lerp(ambientColor1, ambientColor0, saturate(UpdotL * 3.0)); // 0% - 33%
return UpdotL > 0 ? ambientLowSun : ambientTwilight;
}
Binary file not shown.
Loading

0 comments on commit b8dacad

Please sign in to comment.