From 65776aefaaa7f24455cc4b5fa8b3b90c8dd606b7 Mon Sep 17 00:00:00 2001 From: elizabeth-legros <59933602+elizabeth-legros@users.noreply.github.com> Date: Fri, 30 Apr 2021 14:00:50 -0700 Subject: [PATCH] add ability to use texture tiling and offset on texture2ds (#4185) * add ability to use texture tiling and offset on texture2ds * minor code review fixes and adding SplitTextureTransformNode * more code review notes and minor bugfixes * more code review changes and bump subgraph version for reimport * formatting * fixing a bug with the parallax occlusion mapping node, and changing the title of the split texture transform node * fixing node name * make normalfromtexture use tiling correctly, and added copy support for the use tiling flag * formatting * fixing bad merge * fixing more bad merges and a bug I noticed while reviewing output code * Update CHANGELOG.md --- .../ShaderLibrary/Texture.hlsl | 3 ++ com.unity.shadergraph/CHANGELOG.md | 3 ++ .../Data/Graphs/Texture2DShaderProperty.cs | 23 +++++++++-- .../Artistic/Normal/NormalFromTextureNode.cs | 2 +- .../Input/Texture/SampleTexture2DLODNode.cs | 2 +- .../Input/Texture/SampleTexture2DNode.cs | 5 ++- .../Data/Nodes/UV/ParallaxMappingNode.cs | 2 +- .../Nodes/UV/ParallaxOcclusionMappingNode.cs | 4 +- .../Utility/SplitTextureTransformNode.cs | 40 +++++++++++++++++++ .../Utility/SplitTextureTransformNode.cs.meta | 11 +++++ .../ShaderInputPropertyDrawer.cs | 14 +++++++ 11 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 com.unity.shadergraph/Editor/Data/Nodes/Utility/SplitTextureTransformNode.cs create mode 100644 com.unity.shadergraph/Editor/Data/Nodes/Utility/SplitTextureTransformNode.cs.meta diff --git a/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl b/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl index 7a5ac40978c..aff47fc416b 100644 --- a/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl +++ b/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl @@ -47,6 +47,8 @@ struct UnityTexture2D float4 SampleBias(UnitySamplerState s, float2 uv, float bias) { return SAMPLE_TEXTURE2D_BIAS(tex, s.samplerstate, uv, bias); } float4 SampleGrad(UnitySamplerState s, float2 uv, float2 dpdx, float2 dpdy) { return SAMPLE_TEXTURE2D_GRAD(tex, s.samplerstate, uv, dpdx, dpdy); } + float2 GetTransformedUV(float2 uv) { return uv * scaleTranslate.xy + scaleTranslate.zw; } + #ifndef SHADER_API_GLES float CalculateLevelOfDetail(UnitySamplerState s, float2 uv) { return CALCULATE_TEXTURE2D_LOD(tex, s.samplerstate, uv); } @@ -92,6 +94,7 @@ UnityTexture2D UnityBuildTexture2DStructInternal(TEXTURE2D_PARAM(tex, samplersta return result; } + struct UnityTexture2DArray { TEXTURE2D_ARRAY(tex); diff --git a/com.unity.shadergraph/CHANGELOG.md b/com.unity.shadergraph/CHANGELOG.md index 1d9348d9878..abbc18a3504 100644 --- a/com.unity.shadergraph/CHANGELOG.md +++ b/com.unity.shadergraph/CHANGELOG.md @@ -21,6 +21,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added `Use Custom Binding` option to properties. When this option is enabled, a property can be connected to a `Branch On Input Connection` node. The user provides a custom label that will be displayed on the exposed property, when it is disconnected in a graph. - Added new dropdown property type for subgraphs, to allow compile time branching that can be controlled from the parent graph, via the subgraph instance node. - Added `Dropdown` node per dropdown property, that can be used to configure the desired branch control. + - Added the ability to mark textures / colors as \[MainTexture\] and \[MainColor\]. + - Added the ability to enable tiling and offset controls for a Texture2D input. + - Added the Split Texture Transform node to allow using/overriding the provided tiling and offset from a texture input. ### Changed - Updated/corrected View Direction doc diff --git a/com.unity.shadergraph/Editor/Data/Graphs/Texture2DShaderProperty.cs b/com.unity.shadergraph/Editor/Data/Graphs/Texture2DShaderProperty.cs index 2aa48254aea..f06475ae144 100644 --- a/com.unity.shadergraph/Editor/Data/Graphs/Texture2DShaderProperty.cs +++ b/com.unity.shadergraph/Editor/Data/Graphs/Texture2DShaderProperty.cs @@ -46,12 +46,16 @@ internal Texture2DShaderProperty() internal string modifiableTagString => modifiable ? "" : "[NonModifiableTextureData]"; + [SerializeField] + internal bool useTilingAndOffset = false; + + internal string useSTString => useTilingAndOffset ? "" : "[NoScaleOffset]"; internal string mainTextureString => isMainTexture ? "[MainTexture]" : ""; internal override string GetPropertyBlockString() { var normalTagString = (defaultType == DefaultType.NormalMap) ? "[Normal]" : ""; - return $"{hideTagString}{modifiableTagString}{normalTagString}{mainTextureString}[NoScaleOffset]{referenceName}(\"{displayName}\", 2D) = \"{ToShaderLabString(defaultType)}\" {{}}"; + return $"{hideTagString}{modifiableTagString}{normalTagString}{mainTextureString}{useSTString}{referenceName}(\"{displayName}\", 2D) = \"{ToShaderLabString(defaultType)}\" {{}}"; } // Texture2D properties cannot be set via Hybrid path at the moment; disallow that choice @@ -64,7 +68,10 @@ internal override void ForeachHLSLProperty(Action action) action(new HLSLProperty(HLSLType._Texture2D, referenceName, HLSLDeclaration.Global)); action(new HLSLProperty(HLSLType._SamplerState, "sampler" + referenceName, HLSLDeclaration.Global)); action(new HLSLProperty(HLSLType._float4, referenceName + "_TexelSize", decl)); - // action(new HLSLProperty(HLSLType._float4, referenceName + "_ST", decl)); // TODO: allow users to make use of the ST values + if (useTilingAndOffset) + { + action(new HLSLProperty(HLSLType._float4, referenceName + "_ST", decl)); + } } internal override string GetPropertyAsArgumentString(string precisionString) @@ -82,7 +89,16 @@ internal override string GetHLSLVariableName(bool isSubgraphProperty) if (isSubgraphProperty) return referenceName; else - return $"UnityBuildTexture2DStructNoScale({referenceName})"; + { + if (useTilingAndOffset) + { + return $"UnityBuildTexture2DStruct({referenceName})"; + } + else + { + return $"UnityBuildTexture2DStructNoScale({referenceName})"; + } + } } [SerializeField] @@ -125,6 +141,7 @@ internal override ShaderInput Copy() displayName = displayName, value = value, defaultType = defaultType, + useTilingAndOffset = useTilingAndOffset, isMainTexture = isMainTexture }; } diff --git a/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Normal/NormalFromTextureNode.cs b/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Normal/NormalFromTextureNode.cs index 008e00d6b0e..bee26c3c575 100644 --- a/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Normal/NormalFromTextureNode.cs +++ b/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Normal/NormalFromTextureNode.cs @@ -64,7 +64,7 @@ public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMo samplerValue = textureValue; sb.AppendLine("{0} {1};", FindOutputSlot(OutputSlotId).concreteValueType.ToShaderString(), GetVariableNameForSlot(OutputSlotId)); - sb.AppendLine("{0}(TEXTURE2D_ARGS({1}.tex, {2}.samplerstate), {3}, {4}, {5}, {6});", GetFunctionName(), textureValue, samplerValue, uvValue, offsetValue, strengthValue, outputValue); + sb.AppendLine("{0}(TEXTURE2D_ARGS({1}.tex, {2}.samplerstate), {1}.GetTransformedUV({3}), {4}, {5}, {6});", GetFunctionName(), textureValue, samplerValue, uvValue, offsetValue, strengthValue, outputValue); } public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode) diff --git a/com.unity.shadergraph/Editor/Data/Nodes/Input/Texture/SampleTexture2DLODNode.cs b/com.unity.shadergraph/Editor/Data/Nodes/Input/Texture/SampleTexture2DLODNode.cs index 6865f0a345c..32ee6ed1776 100644 --- a/com.unity.shadergraph/Editor/Data/Nodes/Input/Texture/SampleTexture2DLODNode.cs +++ b/com.unity.shadergraph/Editor/Data/Nodes/Input/Texture/SampleTexture2DLODNode.cs @@ -116,7 +116,7 @@ public virtual void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode gene } sb.AppendLine("#else"); { - var result = string.Format(" $precision4 {0} = SAMPLE_TEXTURE2D_LOD({1}.tex, {2}.samplerstate, {3}, {4});" + var result = string.Format(" $precision4 {0} = SAMPLE_TEXTURE2D_LOD({1}.tex, {2}.samplerstate, {1}.GetTransformedUV({3}), {4});" , GetVariableNameForSlot(OutputSlotRGBAId) , id , edgesSampler.Any() ? GetSlotValue(SamplerInputId, generationMode) : id diff --git a/com.unity.shadergraph/Editor/Data/Nodes/Input/Texture/SampleTexture2DNode.cs b/com.unity.shadergraph/Editor/Data/Nodes/Input/Texture/SampleTexture2DNode.cs index 77dab14df2d..882a849ba07 100644 --- a/com.unity.shadergraph/Editor/Data/Nodes/Input/Texture/SampleTexture2DNode.cs +++ b/com.unity.shadergraph/Editor/Data/Nodes/Input/Texture/SampleTexture2DNode.cs @@ -108,7 +108,8 @@ public virtual void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode gene var edgesSampler = owner.GetEdges(samplerSlot.slotReference); var id = GetSlotValue(TextureInputId, generationMode); - var result = string.Format("$precision4 {0} = SAMPLE_TEXTURE2D({1}.tex, {2}.samplerstate, {3});" + + var result = string.Format("$precision4 {0} = SAMPLE_TEXTURE2D({1}.tex, {2}.samplerstate, {1}.GetTransformedUV({3}));" , GetVariableNameForSlot(OutputSlotRGBAId) , id , edgesSampler.Any() ? GetSlotValue(SamplerInput, generationMode) : id @@ -129,6 +130,8 @@ public virtual void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode gene } sb.AppendLine(string.Format("$precision {0} = {1}.r;", GetVariableNameForSlot(OutputSlotRId), GetVariableNameForSlot(OutputSlotRGBAId))); + + sb.AppendLine(string.Format("$precision {0} = {1}.g;", GetVariableNameForSlot(OutputSlotGId), GetVariableNameForSlot(OutputSlotRGBAId))); sb.AppendLine(string.Format("$precision {0} = {1}.b;", GetVariableNameForSlot(OutputSlotBId), GetVariableNameForSlot(OutputSlotRGBAId))); sb.AppendLine(string.Format("$precision {0} = {1}.a;", GetVariableNameForSlot(OutputSlotAId), GetVariableNameForSlot(OutputSlotRGBAId))); diff --git a/com.unity.shadergraph/Editor/Data/Nodes/UV/ParallaxMappingNode.cs b/com.unity.shadergraph/Editor/Data/Nodes/UV/ParallaxMappingNode.cs index 1b86815ed7b..259e96bdeb9 100644 --- a/com.unity.shadergraph/Editor/Data/Nodes/UV/ParallaxMappingNode.cs +++ b/com.unity.shadergraph/Editor/Data/Nodes/UV/ParallaxMappingNode.cs @@ -70,7 +70,7 @@ public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMo var amplitude = GetSlotValue(kAmplitudeSlotId, generationMode); var uvs = GetSlotValue(kUVsSlotId, generationMode); - sb.AppendLines(String.Format(@"$precision2 {5} = {4} + ParallaxMapping(TEXTURE2D_ARGS({0}.tex, {1}.samplerstate), IN.{2}, {3} * 0.01, {4});", + sb.AppendLines(String.Format(@"$precision2 {5} = {0}.GetTransformedUV({4}) + ParallaxMapping(TEXTURE2D_ARGS({0}.tex, {1}.samplerstate), IN.{2}, {3} * 0.01, {0}.GetTransformedUV({4}));", heightmap, edgesSampler.Any() ? GetSlotValue(kHeightmapSamplerSlotId, generationMode) : heightmap, CoordinateSpace.Tangent.ToVariableName(InterpolatorType.ViewDirection), diff --git a/com.unity.shadergraph/Editor/Data/Nodes/UV/ParallaxOcclusionMappingNode.cs b/com.unity.shadergraph/Editor/Data/Nodes/UV/ParallaxOcclusionMappingNode.cs index 2f22d82f7d4..4af9c63da22 100644 --- a/com.unity.shadergraph/Editor/Data/Nodes/UV/ParallaxOcclusionMappingNode.cs +++ b/com.unity.shadergraph/Editor/Data/Nodes/UV/ParallaxOcclusionMappingNode.cs @@ -164,7 +164,7 @@ public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMo $precision3 {tmpViewDirUV} = normalize($precision3({tmpViewDir}.xy * {tmpMaxHeight}, {tmpViewDir}.z)); // TODO: skip normalize PerPixelHeightDisplacementParam {tmpPOMParam}; -{tmpPOMParam}.uv = {uvs};"); +{tmpPOMParam}.uv = {heightmap}.GetTransformedUV({uvs});"); // to avoid crashes when steps gets too big, and // to avoid divide by zero, we clamp it to the range [1, 256] @@ -173,7 +173,7 @@ public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMo sb.AppendLines($@" $precision {tmpOutHeight}; -$precision2 {GetVariableNameForSlot(kParallaxUVsOutputSlotId)} = {uvs} + ParallaxOcclusionMapping{GetFunctionName()}({lod}, {lodThreshold}, {steps}, {tmpViewDirUV}, {tmpPOMParam}, {tmpOutHeight}, TEXTURE2D_ARGS({heightmap}.tex, {sampler}.samplerstate)); +$precision2 {GetVariableNameForSlot(kParallaxUVsOutputSlotId)} = {heightmap}.GetTransformedUV({uvs}) + ParallaxOcclusionMapping{GetFunctionName()}({lod}, {lodThreshold}, {steps}, {tmpViewDirUV}, {tmpPOMParam}, {tmpOutHeight}, TEXTURE2D_ARGS({heightmap}.tex, {sampler}.samplerstate)); $precision {GetVariableNameForSlot(kPixelDepthOffsetOutputSlotId)} = ({tmpMaxHeight} - {tmpOutHeight} * {tmpMaxHeight}) / max({tmpNdotV}, 0.0001); "); diff --git a/com.unity.shadergraph/Editor/Data/Nodes/Utility/SplitTextureTransformNode.cs b/com.unity.shadergraph/Editor/Data/Nodes/Utility/SplitTextureTransformNode.cs new file mode 100644 index 00000000000..f9dbb2765db --- /dev/null +++ b/com.unity.shadergraph/Editor/Data/Nodes/Utility/SplitTextureTransformNode.cs @@ -0,0 +1,40 @@ +using System.Reflection; +using UnityEngine; + +namespace UnityEditor.ShaderGraph +{ + [Title("Input", "Texture", "Split Texture Transform")] + class SplitTextureTransformNode : CodeFunctionNode + { + public override bool hasPreview { get { return false; } } + public SplitTextureTransformNode() + { + name = "Split Texture Transform"; + } + + protected override MethodInfo GetFunctionToConvert() + { + return GetType().GetMethod("Unity_SplitTextureTransform", BindingFlags.Static | BindingFlags.NonPublic); + } + + static string Unity_SplitTextureTransform( + [Slot(0, Binding.None)] Texture2D In, + [Slot(1, Binding.None)] out Vector2 Tiling, + [Slot(2, Binding.None)] out Vector2 Offset, + [Slot(3, Binding.None)] out Texture2D TextureOnly) + { + TextureOnly = default; + Tiling = default; + Offset = default; + return +@" +{ + TextureOnly = In; + TextureOnly.scaleTranslate = float4(1.0f, 1.0f, 0.0f, 0.0f); + Tiling = In.scaleTranslate.xy; + Offset = In.scaleTranslate.zw; +} +"; + } + } +} diff --git a/com.unity.shadergraph/Editor/Data/Nodes/Utility/SplitTextureTransformNode.cs.meta b/com.unity.shadergraph/Editor/Data/Nodes/Utility/SplitTextureTransformNode.cs.meta new file mode 100644 index 00000000000..d7bc03158c9 --- /dev/null +++ b/com.unity.shadergraph/Editor/Data/Nodes/Utility/SplitTextureTransformNode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88d9d4ff293792c42a6008e688bbc8fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.shadergraph/Editor/Drawing/Inspector/PropertyDrawers/ShaderInputPropertyDrawer.cs b/com.unity.shadergraph/Editor/Drawing/Inspector/PropertyDrawers/ShaderInputPropertyDrawer.cs index ff225264d07..1e3d8937643 100644 --- a/com.unity.shadergraph/Editor/Drawing/Inspector/PropertyDrawers/ShaderInputPropertyDrawer.cs +++ b/com.unity.shadergraph/Editor/Drawing/Inspector/PropertyDrawers/ShaderInputPropertyDrawer.cs @@ -823,6 +823,20 @@ out var texture2DField out var textureModeField)); textureModeField.SetEnabled(texture2DProperty.generatePropertyBlock); + + var togglePropertyDrawer = new ToggleDataPropertyDrawer(); + propertySheet.Add(togglePropertyDrawer.CreateGUI( + newValue => + { + this._preChangeValueCallback("Change Use Tilling and Offset"); + if (texture2DProperty.useTilingAndOffset == newValue.isOn) + return; + texture2DProperty.useTilingAndOffset = newValue.isOn; + this._postChangeValueCallback(); + }, + new ToggleData(texture2DProperty.useTilingAndOffset, true), + "Use Tiling and Offset", + out var tilingAndOffsetToggle)); } }