private static void ComputeCompressionScalers(ProceduralTexture2D target) { target.compressionScalers = Vector4.one; if (target.compressionQuality != ProceduralTexture2D.CompressionLevel.None && target.type != ProceduralTexture2D.TextureType.Other) { target.compressionScalers.x = 1.0f / target.colorSpaceVector1.magnitude; target.compressionScalers.y = 1.0f / target.colorSpaceVector2.magnitude; target.compressionScalers.z = 1.0f / target.colorSpaceVector3.magnitude; } }
private static void PreprocessData(ProceduralTexture2D target) { if (target.input == null) { return; } // Init progress bar stepCounter = 0; totalSteps = (target.type != ProceduralTexture2D.TextureType.Other ? 4 : 0) + (target.type != ProceduralTexture2D.TextureType.Other ? 9 : 12) + 1; EditorUtility.DisplayProgressBar("Pre-processing Procedural Texture Data", target.name, (float)stepCounter / (totalSteps - 1)); // Section 1.4 Improvement: using a decorrelated color space for Color RGB and Normal XYZ textures TextureFormat inputFormat = TextureFormat.RGB24; TextureData albedoData = TextureToTextureData(target.input, ref inputFormat); TextureData decorrelated = new TextureData(albedoData); if (target.type != ProceduralTexture2D.TextureType.Other) { DecorrelateColorSpace(ref albedoData, ref decorrelated, ref target.colorSpaceVector1, ref target.colorSpaceVector2, ref target.colorSpaceVector3, ref target.colorSpaceOrigin, target.name); } ComputeCompressionScalers(target); // Perform precomputations TextureData Tinput = new TextureData(decorrelated.width, decorrelated.height); TextureData invT = new TextureData(LUT_WIDTH, (int)(Mathf.Log((float)Tinput.width) / Mathf.Log(2.0f))); // Height = Number of prefiltered LUT levels List <int> channelsToProcess = new List <int> { 0, 1, 2 }; if ((target.type == ProceduralTexture2D.TextureType.Color && target.includeAlpha == true) || target.type == ProceduralTexture2D.TextureType.Other) { channelsToProcess.Add(3); } Precomputations(ref decorrelated, channelsToProcess, ref Tinput, ref invT, target.name); RescaleForCompression(target, ref Tinput); EditorUtility.DisplayProgressBar("Pre-processing Procedural Texture Data", target.name, (float)stepCounter++ / (totalSteps - 1)); // Serialize precomputed data and setup material FinalizePrecomputedTextures(ref inputFormat, target, ref Tinput, ref invT); target.memoryUsageBytes = target.Tinput.GetRawTextureData().Length + target.invT.GetRawTextureData().Length; EditorUtility.ClearProgressBar(); // Update current applied settings target.currentInput = target.input; target.currentIncludeAlpha = target.includeAlpha; target.currentGenerateMipMaps = target.generateMipMaps; target.currentFilterMode = target.filterMode; target.currentAnisoLevel = target.anisoLevel; target.currentCompressionQuality = target.compressionQuality; }
static void FinalizePrecomputedTextures(ref TextureFormat inputFormat, ProceduralTexture2D target, ref TextureData Tinput, ref TextureData invT) { // Serialize precomputed data as new subasset texture. Reuse existing texture if possible to avoid breaking texture references in shadergraph. if (target.Tinput == null) { target.Tinput = new Texture2D(Tinput.width, Tinput.height, inputFormat, target.generateMipMaps, true); AssetDatabase.AddObjectToAsset(target.Tinput, target); } target.Tinput.Resize(Tinput.width, Tinput.height, inputFormat, target.generateMipMaps); target.Tinput.name = target.input.name + "_T"; target.Tinput.SetPixels(Tinput.data); target.Tinput.wrapMode = TextureWrapMode.Repeat; target.Tinput.filterMode = target.filterMode; target.Tinput.anisoLevel = target.anisoLevel; target.Tinput.Apply(); if (target.compressionQuality != ProceduralTexture2D.CompressionLevel.None) { if (target.compressionQuality == ProceduralTexture2D.CompressionLevel.HighQuality) { EditorUtility.CompressTexture(target.Tinput, TextureFormat.BC7, (int)target.compressionQuality); } else if (inputFormat == TextureFormat.RGBA32) { EditorUtility.CompressTexture(target.Tinput, TextureFormat.DXT5, (int)target.compressionQuality); } else { EditorUtility.CompressTexture(target.Tinput, TextureFormat.DXT1, (int)target.compressionQuality); } } target.Tinput.Apply(); if (target.invT == null) { target.invT = new Texture2D(invT.width, invT.height, inputFormat, false, true); AssetDatabase.AddObjectToAsset(target.invT, target); } target.invT.Resize(invT.width, invT.height, inputFormat, false); target.invT.name = target.input.name + "_invT"; target.invT.wrapMode = TextureWrapMode.Clamp; target.invT.filterMode = FilterMode.Bilinear; target.invT.SetPixels(invT.data); target.invT.Apply(); // Update asset database AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); }
private bool UnappliedSettingChanges(ProceduralTexture2D target) { if (target.currentInput != target.input || target.currentIncludeAlpha != target.includeAlpha || target.currentGenerateMipMaps != target.generateMipMaps || target.currentFilterMode != target.filterMode || target.currentAnisoLevel != target.anisoLevel || target.currentCompressionQuality != target.compressionQuality) { return(true); } else { return(false); } }
private void CopyInputTextureImportType(ProceduralTexture2D target) { string path = AssetDatabase.GetAssetPath(target.input); TextureImporter inputImporter = (TextureImporter)TextureImporter.GetAtPath(path); switch (inputImporter.textureType) { case TextureImporterType.NormalMap: target.type = ProceduralTexture2D.TextureType.Normal; break; default: target.type = ProceduralTexture2D.TextureType.Color; break; } }
private static void RescaleForCompression(ProceduralTexture2D target, ref TextureData Tinput) { int channelCount = (target.type == ProceduralTexture2D.TextureType.Color && target.includeAlpha == true) || target.type == ProceduralTexture2D.TextureType.Other ? 4 : 3; // If we use DXT compression // we need to rescale the Gaussian channels (see Section 1.6) if (target.compressionQuality != ProceduralTexture2D.CompressionLevel.None && target.type != ProceduralTexture2D.TextureType.Other) { for (int y = 0; y < Tinput.height; y++) { for (int x = 0; x < Tinput.width; x++) { for (int i = 0; i < channelCount; i++) { float v = Tinput.GetColor(x, y)[i]; v = (v - 0.5f) / target.compressionScalers[i] + 0.5f; Tinput.GetColorRef(x, y)[i] = v; } } } } }
public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode) { // } // // // Node generations // public virtual void GenerateNodeCode(ShaderGenerator visitor, GraphContext graphContext, GenerationMode generationMode) // { ProceduralTexture2DInputMaterialSlot slot = FindInputSlot <ProceduralTexture2DInputMaterialSlot>(ProceduralTexture2DId); // Find Procedural Texture 2D Asset ProceduralTexture2D proceduralTexture2D = slot.proceduralTexture2D; var edges = owner.GetEdges(slot.slotReference).ToArray(); if (edges.Any()) { var fromSocketRef = edges[0].outputSlot; #if UNITY_2020_2_OR_NEWER var fromNode = owner.GetNodeFromId <ProceduralTexture2DNode>(fromSocketRef.node.objectId); #else var fromNode = owner.GetNodeFromGuid <ProceduralTexture2DNode>(fromSocketRef.nodeGuid); #endif if (fromNode != null) { proceduralTexture2D = fromNode.proceduralTexture2D; } } var precision = concretePrecision.ToShaderString(); // No Procedural Texture 2D Asset found, break and initialize output values to default white if (proceduralTexture2D == null || proceduralTexture2D.Tinput == null || proceduralTexture2D.invT == null) { sb.AppendLine("{0}4 {1} = float4(1, 1, 1, 1);", precision, GetVariableNameForSlot(OutputSlotRGBAId)); sb.AppendLine("{0} {1} = {2}.r;", precision, GetVariableNameForSlot(OutputSlotRId), GetVariableNameForSlot(OutputSlotRGBAId)); sb.AppendLine("{0} {1} = {2}.g;", precision, GetVariableNameForSlot(OutputSlotGId), GetVariableNameForSlot(OutputSlotRGBAId)); sb.AppendLine("{0} {1} = {2}.b;", precision, GetVariableNameForSlot(OutputSlotBId), GetVariableNameForSlot(OutputSlotRGBAId)); sb.AppendLine("{0} {1} = {2}.a;", precision, GetVariableNameForSlot(OutputSlotAId), GetVariableNameForSlot(OutputSlotRGBAId)); return; } // Apply hidden inputs stored in Procedural Texture 2D Asset to shader FindInputSlot <Texture2DInputMaterialSlot>(TinputId).texture = proceduralTexture2D.Tinput; FindInputSlot <Texture2DInputMaterialSlot>(InvTinputId).texture = proceduralTexture2D.invT; FindInputSlot <Vector4MaterialSlot>(CompressionScalersId).value = proceduralTexture2D.compressionScalers; FindInputSlot <Vector3MaterialSlot>(ColorSpaceOriginId).value = proceduralTexture2D.colorSpaceOrigin; FindInputSlot <Vector3MaterialSlot>(ColorSpaceVector1Id).value = proceduralTexture2D.colorSpaceVector1; FindInputSlot <Vector3MaterialSlot>(ColorSpaceVector2Id).value = proceduralTexture2D.colorSpaceVector2; FindInputSlot <Vector3MaterialSlot>(ColorSpaceVector3Id).value = proceduralTexture2D.colorSpaceVector3; FindInputSlot <Vector3MaterialSlot>(InputSizeId).value = new Vector3( proceduralTexture2D.Tinput.width, proceduralTexture2D.Tinput.height, proceduralTexture2D.invT.height); string code = @" float4 {9} = float4(0, 0, 0, 0); { float2 uvScaled = {0} * 3.464; // 2 * sqrt(3) const float2x2 gridToSkewedGrid = float2x2(1.0, 0.0, -0.57735027, 1.15470054); float2 skewedCoord = mul(gridToSkewedGrid, uvScaled); int2 baseId = int2(floor(skewedCoord)); float3 temp = float3(frac(skewedCoord), 0); temp.z = 1.0 - temp.x - temp.y; float w1, w2, w3; int2 vertex1, vertex2, vertex3; if (temp.z > 0.0) { w1 = temp.z; w2 = temp.y; w3 = temp.x; vertex1 = baseId; vertex2 = baseId + int2(0, 1); vertex3 = baseId + int2(1, 0); } else { w1 = -temp.z; w2 = 1.0 - temp.y; w3 = 1.0 - temp.x; vertex1 = baseId + int2(1, 1); vertex2 = baseId + int2(1, 0); vertex3 = baseId + int2(0, 1); } float2 uv1 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), (float2)vertex1)) * 43758.5453); float2 uv2 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), (float2)vertex2)) * 43758.5453); float2 uv3 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), (float2)vertex3)) * 43758.5453); float2 duvdx = ddx({0}); float2 duvdy = ddy({0}); float4 G1 = {1}.SampleGrad({10}, uv1, duvdx, duvdy); float4 G2 = {1}.SampleGrad({10}, uv2, duvdx, duvdy); float4 G3 = {1}.SampleGrad({10}, uv3, duvdx, duvdy); float exponent = 1.0 + {11} * 15.0; w1 = pow(w1, exponent); w2 = pow(w2, exponent); w3 = pow(w3, exponent); float sum = w1 + w2 + w3; w1 = w1 / sum; w2 = w2 / sum; w3 = w3 / sum; float4 G = w1 * G1 + w2 * G2 + w3 * G3; G = G - 0.5; G = G * rsqrt(w1 * w1 + w2 * w2 + w3 * w3); G = G * {3}; G = G + 0.5; duvdx *= {8}.xy; duvdy *= {8}.xy; float delta_max_sqr = max(dot(duvdx, duvdx), dot(duvdy, duvdy)); float mml = 0.5 * log2(delta_max_sqr); float LOD = max(0, mml) / {8}.z; {9}.r = {2}.SampleLevel(sampler{2}, float2(G.r, LOD), 0).r; {9}.g = {2}.SampleLevel(sampler{2}, float2(G.g, LOD), 0).g; {9}.b = {2}.SampleLevel(sampler{2}, float2(G.b, LOD), 0).b; {9}.a = {2}.SampleLevel(sampler{2}, float2(G.a, LOD), 0).a; } " ; if (proceduralTexture2D != null && proceduralTexture2D.type != ProceduralTexture2D.TextureType.Other) { code += "{9}.rgb = {4} + {5} * {9}.r + {6} * {9}.g + {7} * {9}.b;"; } if (proceduralTexture2D != null && proceduralTexture2D.type == ProceduralTexture2D.TextureType.Normal) { code += "{9}.rgb = UnpackNormalmapRGorAG({9});"; } code = code.Replace("{0}", GetSlotValue(UVInput, generationMode)); code = code.Replace("{1}", GetSlotValue(TinputId, generationMode)); code = code.Replace("{2}", GetSlotValue(InvTinputId, generationMode)); code = code.Replace("{3}", GetSlotValue(CompressionScalersId, generationMode)); code = code.Replace("{4}", GetSlotValue(ColorSpaceOriginId, generationMode)); code = code.Replace("{5}", GetSlotValue(ColorSpaceVector1Id, generationMode)); code = code.Replace("{6}", GetSlotValue(ColorSpaceVector2Id, generationMode)); code = code.Replace("{7}", GetSlotValue(ColorSpaceVector3Id, generationMode)); code = code.Replace("{8}", GetSlotValue(InputSizeId, generationMode)); code = code.Replace("{9}", GetVariableNameForSlot(OutputSlotRGBAId)); var edgesSampler = owner.GetEdges(FindInputSlot <MaterialSlot>(SamplerInput).slotReference); code = code.Replace("{10}", edgesSampler.Any() ? GetSlotValue(SamplerInput, generationMode) : "sampler" + GetSlotValue(TinputId, generationMode)); code = code.Replace("{11}", GetSlotValue(BlendId, generationMode)); sb.AppendLine(code); sb.AppendLine("{0} {1} = {2}.r;", precision, GetVariableNameForSlot(OutputSlotRId), GetVariableNameForSlot(OutputSlotRGBAId)); sb.AppendLine("{0} {1} = {2}.g;", precision, GetVariableNameForSlot(OutputSlotGId), GetVariableNameForSlot(OutputSlotRGBAId)); sb.AppendLine("{0} {1} = {2}.b;", precision, GetVariableNameForSlot(OutputSlotBId), GetVariableNameForSlot(OutputSlotRGBAId)); sb.AppendLine("{0} {1} = {2}.a;", precision, GetVariableNameForSlot(OutputSlotAId), GetVariableNameForSlot(OutputSlotRGBAId)); }