public static void CopyTexture(CommandBuffer cmd, Texture source, Texture destination, int mipStart, int mipStop = -1) { if (mipStop == -1) { mipStop = mipStart + 1; } using (new ProfilingScope(cmd, new ProfilingSampler("Copy Texture " + source.name + " to " + destination.name))) { int originalSliceCount = (source.dimension == TextureDimension.Cube) ? 6 : TextureUtils.GetSliceCount(source); bool canCopy = source.graphicsFormat == destination.graphicsFormat && source.width == destination.width && source.height == destination.height; if (canCopy) { for (int mipLevel = mipStart; mipLevel < mipStop; mipLevel++) { int sliceCount = source.dimension == TextureDimension.Tex3D ? Mathf.Max(originalSliceCount >> mipLevel, 1) : originalSliceCount; for (int slice = 0; slice < sliceCount; slice++) { // CopyTexture with mip API is really weird, it needs to take the slice << mipLevel otherwise it doesn't work int copySlice = source.dimension == TextureDimension.Tex3D ? slice << mipLevel : slice; cmd.CopyTexture(source, copySlice, mipLevel, destination, copySlice, mipLevel); } } } else { // no mips mip target in Blit call for (int slice = 0; slice < originalSliceCount; slice++) { cmd.Blit(source, destination, slice, 0); } } } }
public static void ComputeHistogram(CommandBuffer cmd, Texture input, HistogramData data) { using (new ProfilingScope(cmd, new ProfilingSampler("Generate Histogram"))) { MixtureUtils.SetupComputeTextureDimension(cmd, histogramCompute, input.dimension); // Clear buffers cmd.SetComputeBufferParam(histogramCompute, clearKernel, "_ImageLuminance", luminanceBuffer); cmd.SetComputeBufferParam(histogramCompute, clearKernel, "_Histogram", data.histogram); int dispatchCount = input.width * input.height * TextureUtils.GetSliceCount(input) / 64; // Limit computing to 8K textures int yCount = Mathf.Clamp(dispatchCount / dispatchGroupSizeX / 64, 1, dispatchGroupSizeX); int xCount = Mathf.Clamp(dispatchCount / yCount / 8, 1, dispatchGroupSizeX); cmd.SetComputeIntParam(histogramCompute, "_DispatchSizeX", dispatchGroupSizeX); cmd.DispatchCompute(histogramCompute, clearKernel, xCount, yCount, 1); // Find luminance min / max in the texture // TODO: handle texture 3D and Cube cmd.SetComputeTextureParam(histogramCompute, computeLuminanceBufferKernel, "_Input", input); cmd.SetComputeBufferParam(histogramCompute, computeLuminanceBufferKernel, "_ImageLuminance", luminanceBuffer); cmd.SetComputeVectorParam(histogramCompute, "_InputTextureSize", new Vector4(input.width, input.height, TextureUtils.GetSliceCount(input), 0)); cmd.SetComputeVectorParam(histogramCompute, "_RcpTextureSize", new Vector4(1.0f / input.width, 1.0f / input.height, 1.0f / TextureUtils.GetSliceCount(input), 0)); MixtureUtils.SetTextureWithDimension(cmd, histogramCompute, computeLuminanceBufferKernel, "_Input", input); cmd.DispatchCompute(histogramCompute, computeLuminanceBufferKernel, Mathf.Max(1, input.width / 8), Mathf.Max(1, input.height / 8), TextureUtils.GetSliceCount(input)); ReduceLuminanceBuffer(cmd, dispatchCount); // Generate histogram data in compute buffer cmd.SetComputeBufferParam(histogramCompute, generateHistogramKernel, "_ImageLuminance", luminanceBuffer); cmd.SetComputeBufferParam(histogramCompute, generateHistogramKernel, "_Histogram", data.histogram); cmd.SetComputeTextureParam(histogramCompute, generateHistogramKernel, "_Input", input); cmd.SetComputeIntParam(histogramCompute, "_HistogramBucketCount", data.bucketCount); cmd.SetComputeVectorParam(histogramCompute, "_RcpTextureSize", new Vector4(1.0f / input.width, 1.0f / input.height, 1.0f / TextureUtils.GetSliceCount(input), 0)); MixtureUtils.SetTextureWithDimension(cmd, histogramCompute, generateHistogramKernel, "_Input", input); cmd.DispatchCompute(histogramCompute, generateHistogramKernel, Mathf.Max(1, input.width / 8), Mathf.Max(1, input.height / 8), TextureUtils.GetSliceCount(input)); cmd.SetComputeBufferParam(histogramCompute, computeHistogramDataKernel, "_HistogramData", data.histogramData); cmd.SetComputeBufferParam(histogramCompute, computeHistogramDataKernel, "_Histogram", data.histogram); cmd.DispatchCompute(histogramCompute, computeHistogramDataKernel, Mathf.Max(1, data.bucketCount / 64), 1, 1); // Request histogram data back for inspector cmd.RequestAsyncReadback(luminanceBuffer, 8, 0, (c) => { var d = c.GetData <float>(); if (d.Length > 0) { data.minLuminance = d[0]; data.maxLuminance = d[1]; } }); } }
public static Texture DuplicateTexture(Texture source, bool copyContent = true) { TextureCreationFlags flags = source.mipmapCount > 1 ? TextureCreationFlags.MipChain : TextureCreationFlags.None; switch (source) { case Texture2D t2D: var new2D = new Texture2D(t2D.width, t2D.height, t2D.graphicsFormat, t2D.mipmapCount, flags); CopyCommonTextureSettings(source, new2D); if (copyContent) { for (int mipLevel = 0; mipLevel < t2D.mipmapCount; mipLevel++) { new2D.SetPixelData(t2D.GetPixelData <byte>(mipLevel), mipLevel); } } return(new2D); case Texture3D t3D: var new3D = new Texture3D(t3D.width, t3D.height, t3D.depth, t3D.graphicsFormat, flags, t3D.mipmapCount); CopyCommonTextureSettings(source, new3D); if (copyContent) { for (int mipLevel = 0; mipLevel < t3D.mipmapCount; mipLevel++) { new3D.SetPixelData(t3D.GetPixelData <byte>(mipLevel), mipLevel); } } return(new3D); case Cubemap cube: var newCube = new Cubemap(cube.width, cube.graphicsFormat, flags, cube.mipmapCount); CopyCommonTextureSettings(source, newCube); if (copyContent) { for (int slice = 0; slice < 6; slice++) { for (int mipLevel = 0; mipLevel < cube.mipmapCount; mipLevel++) { newCube.SetPixelData(cube.GetPixelData <byte>(mipLevel, (CubemapFace)slice), mipLevel, (CubemapFace)slice); } } } return(newCube); case CustomRenderTexture rt: var newRT = new CustomRenderTexture(rt.width, rt.height, rt.graphicsFormat); newRT.dimension = rt.dimension; newRT.depth = rt.depth; newRT.volumeDepth = rt.volumeDepth; CopyCommonTextureSettings(source, newRT); newRT.enableRandomWrite = rt.enableRandomWrite; if (copyContent) { for (int slice = 0; slice < TextureUtils.GetSliceCount(rt); slice++) { for (int mipLevel = 0; mipLevel < rt.mipmapCount; mipLevel++) { Graphics.CopyTexture(rt, slice, mipLevel, newRT, slice, mipLevel); } } } return(newRT); default: throw new System.Exception("Can't duplicate texture of type " + source.GetType()); } void CopyCommonTextureSettings(Texture source, Texture destination) { destination.name = source.name; destination.wrapMode = source.wrapMode; destination.filterMode = source.filterMode; destination.wrapModeU = source.wrapModeU; destination.wrapModeV = source.wrapModeV; destination.wrapModeW = source.wrapModeW; destination.anisoLevel = source.anisoLevel; } }
void CreateTexturePreviewImGUI(VisualElement previewContainer, MixtureNode node) { if (node.showPreviewExposure) { var previewExposure = new Slider(0, 10) { label = "Preview EV100", value = node.previewEV100, }; previewExposure.RegisterValueChangedCallback(e => { node.previewEV100 = e.newValue; }); previewContainer.Add(previewExposure); } var previewImageSlice = new IMGUIContainer(() => { if (node.previewTexture == null) { return; } if (node.previewTexture.dimension == TextureDimension.Tex3D) { EditorGUI.BeginChangeCheck(); EditorGUIUtility.labelWidth = 70; node.previewSlice = EditorGUILayout.Slider("3D Slice", node.previewSlice, 0, TextureUtils.GetSliceCount(node.previewTexture) - 1); EditorGUIUtility.labelWidth = 0; if (EditorGUI.EndChangeCheck()) { MarkDirtyRepaint(); } } DrawPreviewCommonSettings(node.previewTexture); Rect previewRect = GetPreviewRect(node.previewTexture); DrawImGUIPreview(node, previewRect, node.previewSlice); DrawTextureInfoHover(previewRect, node.previewTexture); }); previewContainer.Add(previewImageSlice); }
public static void ComputeLuminanceMinMax(CommandBuffer cmd, ComputeBuffer targetBuffer, Texture input) { MixtureUtils.SetupComputeTextureDimension(cmd, histogramCompute, input.dimension); // Clear buffers cmd.SetComputeBufferParam(histogramCompute, clearLuminanceKernel, "_ImageLuminance", luminanceBuffer); int dispatchCount = input.width * input.height * TextureUtils.GetSliceCount(input) / 64; // Limit computing to 8K textures int yCount = Mathf.Clamp(dispatchCount / dispatchGroupSizeX / 64, 1, dispatchGroupSizeX); int xCount = Mathf.Clamp(dispatchCount / yCount / 8, 1, dispatchGroupSizeX); cmd.SetComputeIntParam(histogramCompute, "_DispatchSizeX", dispatchGroupSizeX); cmd.DispatchCompute(histogramCompute, clearLuminanceKernel, xCount, yCount, TextureUtils.GetSliceCount(input)); // Find luminance min / max in the texture cmd.SetComputeTextureParam(histogramCompute, computeLuminanceBufferKernel, "_Input", input); cmd.SetComputeBufferParam(histogramCompute, computeLuminanceBufferKernel, "_ImageLuminance", luminanceBuffer); cmd.SetComputeVectorParam(histogramCompute, "_InputTextureSize", new Vector4(input.width, input.height, TextureUtils.GetSliceCount(input), 0)); MixtureUtils.SetTextureWithDimension(cmd, histogramCompute, computeLuminanceBufferKernel, "_Input", input); cmd.SetComputeVectorParam(histogramCompute, "_RcpTextureSize", new Vector4(1.0f / input.width, 1.0f / input.height, 1.0f / TextureUtils.GetSliceCount(input), 0)); cmd.DispatchCompute(histogramCompute, computeLuminanceBufferKernel, Mathf.Max(1, input.width / 8), Mathf.Max(1, input.height / 8), TextureUtils.GetSliceCount(input)); ReduceLuminanceBuffer(cmd, dispatchCount); cmd.SetComputeBufferParam(histogramCompute, copyMinMaxToBuffer, "_ImageLuminance", luminanceBuffer); cmd.SetComputeBufferParam(histogramCompute, copyMinMaxToBuffer, "_Target", targetBuffer); cmd.DispatchCompute(histogramCompute, copyMinMaxToBuffer, 1, 1, 1); }
protected override bool ProcessNode(CommandBuffer cmd) { if (!base.ProcessNode(cmd) || input == null) { return(false); } cmd.SetComputeBufferCounterValue(vertices, 0); // TODO: non pot texture3Ds cmd.SetComputeVectorParam(computeShader, "_VolumeSize", new Vector4(input.width, input.height, TextureUtils.GetSliceCount(input))); cmd.SetComputeFloatParam(computeShader, "_VoxelResolution", 1); cmd.SetComputeFloatParam(computeShader, "_Threshold", threshold); cmd.SetComputeTextureParam(computeShader, marchingCubes, "_VolumeTexture", input); cmd.SetComputeBufferParam(computeShader, marchingCubes, "_Vertices", vertices); cmd.SetComputeBufferParam(computeShader, marchingCubes, "_Normals", normals); cmd.SetComputeBufferParam(computeShader, marchingCubes, "_Triangles", triangles); DispatchCompute(cmd, marchingCubes, input.width, input.height, TextureUtils.GetSliceCount(input)); MixtureGraphProcessor.AddGPUAndCPUBarrier(cmd); ComputeBuffer.CopyCount(vertices, counterReadback, 0); int[] count = new int[1]; counterReadback.GetData(count); int vertexCount = count[0] * 3; // Readback all buffers Vector3[] vBuffer = new Vector3[vertexCount]; vertices.GetData(vBuffer, 0, 0, vertexCount); int[] iBuffer = new int[vertexCount]; triangles.GetData(iBuffer, 0, 0, vertexCount); var mesh = new Mesh { indexFormat = IndexFormat.UInt32 }; mesh.vertices = vBuffer; mesh.indexFormat = IndexFormat.UInt32; // mesh.triangles = iBuffer; mesh.SetIndices(iBuffer, MeshTopology.Triangles, 0); mesh.RecalculateBounds(); mesh.RecalculateNormals(); mesh.UploadMeshData(false); output = new MixtureMesh { mesh = mesh }; return(true); }
protected override bool ProcessNode(CommandBuffer cmd) { if (!base.ProcessNode(cmd) || textureAsset == null) { return(false); } #if UNITY_EDITOR var importer = UnityEditor.AssetImporter.GetAtPath(UnityEditor.AssetDatabase.GetAssetPath(textureAsset)); if (importer is UnityEditor.TextureImporter textureImporter) { normalMap = textureImporter.textureType == UnityEditor.TextureImporterType.NormalMap; } #endif int targetWidth = textureAsset.width; int targetHeight = textureAsset.height; int targetDepth = TextureUtils.GetSliceCount(textureAsset); bool needsTempTarget = false; if (!IsPowerOf2(textureAsset) && POTMode != PowerOf2Mode.None) { int maxSize = Mathf.Max(Mathf.Max(targetWidth, targetHeight), targetDepth); int potSize = 0; switch (POTMode) { case PowerOf2Mode.ScaleToNextPowerOf2: potSize = Mathf.NextPowerOfTwo(maxSize); break; default: potSize = Mathf.ClosestPowerOfTwo(maxSize); break; } targetWidth = targetHeight = targetDepth = potSize; needsTempTarget = true; } if (normalMap) { needsTempTarget = true; } if (needsTempTarget && postProcessedTexture == null) { postProcessedTexture = new RenderTexture(1, 1, 0, GraphicsFormat.R16G16B16A16_SFloat, mipCount: textureAsset.mipmapCount) { dimension = textureAsset.dimension, enableRandomWrite = true, volumeDepth = 1 } } ; else if (!needsTempTarget) { postProcessedTexture?.Release(); postProcessedTexture = null; } if (postProcessedTexture != null && (postProcessedTexture.width != targetWidth || postProcessedTexture.height != targetHeight || postProcessedTexture.volumeDepth != targetDepth)) { postProcessedTexture.Release(); postProcessedTexture.width = targetWidth; postProcessedTexture.height = targetHeight; postProcessedTexture.volumeDepth = targetDepth; postProcessedTexture.Create(); } // TODO: same alloc as normal map + scale and crop options // Compressed normal maps need to be converted from AG to RG format if (normalMap) { // Transform normal map texture into POT var blitMaterial = GetTempMaterial("Hidden/Mixture/TextureNode"); MixtureUtils.SetTextureWithDimension(blitMaterial, "_Source", textureAsset); blitMaterial.SetInt("_POTMode", (int)POTMode); MixtureUtils.Blit(cmd, blitMaterial, textureAsset, postProcessedTexture, 0); outputTexture = postProcessedTexture; } else if (needsTempTarget) { // Transform standard texture into POT var blitMaterial = GetTempMaterial("Hidden/Mixture/TextureNode"); MixtureUtils.SetTextureWithDimension(blitMaterial, "_Source", textureAsset); blitMaterial.SetInt("_POTMode", (int)POTMode); MixtureUtils.Blit(cmd, blitMaterial, textureAsset, postProcessedTexture, 1); outputTexture = postProcessedTexture; } else { outputTexture = textureAsset; } if (outputTexture != null) { settings.sizeMode = OutputSizeMode.Absolute; settings.width = outputTexture.width; settings.height = outputTexture.height; settings.depth = TextureUtils.GetSliceCount(outputTexture); } return(true); }
void UpdateIsDirtyAndPreview() { if (updateNeededInfoBox == null) { return; } isDirty = variant.IsDirty(); updateNeededInfoBox.style.display = isDirty ? DisplayStyle.Flex : DisplayStyle.None; if (isDirty) { // Copy the result into the inspector preview RT var output = graph.outputNode.outputTextureSettings.FirstOrDefault(n => n.name == defaultTextureEditor.target.name); if (output == null) { output = graph.outputNode.outputTextureSettings.First(); } // Refresh the preview in the inspector: var s = graph.outputNode.rtSettings; if (variantPreview.graphicsFormat != s.graphicsFormat || variantPreview.height != s.height || variantPreview.width != s.width || variantPreview.volumeDepth != s.sliceCount || variantPreview.filterMode != s.filterMode || variantPreview.wrapMode != s.wrapMode || variantPreview.dimension != (TextureDimension)s.dimension || variantPreview.useMipMap != output.hasMipMaps) { variantPreview.Release(); variantPreview.graphicsFormat = s.graphicsFormat; variantPreview.width = s.width; variantPreview.height = s.height; variantPreview.volumeDepth = s.sliceCount; variantPreview.filterMode = s.filterMode; variantPreview.wrapMode = s.wrapMode; variantPreview.dimension = (TextureDimension)s.dimension; variantPreview.name = target.name + "*"; variantPreview.useMipMap = output.hasMipMaps; variantPreview.Create(); } // Update the texture in the inspector variant.ProcessGraphWithOverrides(); TextureUtils.CopyTexture(output.finalCopyRT, variantPreview); // If the parentGraph is opened in the editor, we don't want to mess with previews // so we update the parentGraph with the original params again. if (IsMixtureEditorOpened(graph)) { MixtureGraphProcessor.RunOnce(graph); } if (variantPreviewEditor == null || variantPreviewEditor.target != variantPreview) { Editor.CreateCachedEditor(variantPreview, renderTextureEditorType, ref variantPreviewEditor); } } }
protected override bool ProcessNode() { if (graph.outputTexture == null) { Debug.LogError("Output Node can't write to target texture, Graph references a null output texture"); return(false); } // Update the renderTexture reference for realtime graph if (graph.isRealtime) { if (tempRenderTexture != graph.outputTexture) { onTempRenderTextureUpdated?.Invoke(); } tempRenderTexture = graph.outputTexture as CustomRenderTexture; } var inputPort = GetPort(nameof(input), nameof(input)); if (inputPort.GetEdges().Count == 0) { if (uniqueMessages.Add("OutputNotConnected")) { AddMessage("Output node input is not connected", NodeMessageType.Warning); } input = TextureUtils.GetBlackTexture(rtSettings); // TODO: set a black texture of texture dimension as default value return(false); } else { uniqueMessages.Clear(); ClearMessages(); } // Update the renderTexture size and format: if (UpdateTempRenderTexture(ref tempRenderTexture)) { onTempRenderTextureUpdated?.Invoke(); } if (input.dimension != graph.outputTexture.dimension) { Debug.LogError("Error: Expected texture type input for the OutputNode is " + graph.outputTexture.dimension + " but " + input?.dimension + " was provided"); return(false); } MixtureUtils.SetupDimensionKeyword(finalCopyMaterial, input.dimension); // Manually reset all texture inputs ResetMaterialPropertyToDefault(finalCopyMaterial, "_Source_2D"); ResetMaterialPropertyToDefault(finalCopyMaterial, "_Source_3D"); ResetMaterialPropertyToDefault(finalCopyMaterial, "_Source_Cube"); if (input.dimension == TextureDimension.Tex2D) { finalCopyMaterial.SetTexture("_Source_2D", input); } else if (input.dimension == TextureDimension.Tex3D) { finalCopyMaterial.SetTexture("_Source_3D", input); } else { finalCopyMaterial.SetTexture("_Source_Cube", input); } tempRenderTexture.material = finalCopyMaterial; return(true); }
protected override bool ProcessNode(CommandBuffer cmd) { if (!base.ProcessNode(cmd) || input == null) { return(false); } HistogramUtility.ComputeLuminanceMinMax(cmd, minMaxBuffer, input); TextureUtils.UpdateTextureFromCurve(interpolationCurve, ref curveTexture); var mat = tempRenderTexture.material = GetTempMaterial("Hidden/Mixture/Levels"); mat.SetFloat("_Mode", (int)mode); mat.SetFloat("_ManualMin", min); mat.SetFloat("_ManualMax", max); mat.SetVector("_RcpTextureSize", new Vector4(1.0f / input.width, 1.0f / input.height, 1.0f / TextureUtils.GetSliceCount(input), 0)); MixtureUtils.SetupDimensionKeyword(mat, tempRenderTexture.dimension); MixtureUtils.SetTextureWithDimension(mat, "_Input", input); mat.SetBuffer("_Luminance", minMaxBuffer); mat.SetTexture("_InterpolationCurve", curveTexture); tempRenderTexture.Update(); CustomTextureManager.UpdateCustomRenderTexture(cmd, tempRenderTexture); output = tempRenderTexture; return(true); }
protected override bool ProcessNode(CommandBuffer cmd) { // Force the double buffering for multi-pass flooding rtSettings.doubleBuffered = true; if (!base.ProcessNode(cmd) || input == null) { return(false); } UpdateTempRenderTexture(ref output); cmd.SetComputeFloatParam(computeShader, "_Threshold", threshold); cmd.SetComputeVectorParam(computeShader, "_Size", new Vector4(output.width, 1.0f / output.width)); cmd.SetComputeFloatParam(computeShader, "_Distance", distance / 100.0f); cmd.SetComputeIntParam(computeShader, "_ThresholdMode", (int)thresholdMode); cmd.SetComputeIntParam(computeShader, "_DistanceMode", (int)distanceMode); cmd.SetComputeIntParam(computeShader, "_Mode", (int)mode); output.doubleBuffered = true; output.EnsureDoubleBufferConsistency(); var rt = output.GetDoubleBufferRenderTexture(); if (!rt.enableRandomWrite) { rt.Release(); rt.enableRandomWrite = true; rt.Create(); } MixtureUtils.SetupComputeTextureDimension(cmd, computeShader, input.dimension); MixtureUtils.SetTextureWithDimension(cmd, computeShader, fillUvKernel, "_Input", input); MixtureUtils.SetTextureWithDimension(cmd, computeShader, fillUvKernel, "_Output", output); MixtureUtils.SetTextureWithDimension(cmd, computeShader, fillUvKernel, "_FinalOutput", rt); cmd.SetComputeIntParam(computeShader, "_DistanceMode", (int)distanceMode); cmd.SetComputeFloatParam(computeShader, "_InputScaleFactor", (float)input.width / (float)output.width); DispatchCompute(cmd, fillUvKernel, output.width, output.height, output.volumeDepth); int maxLevels = (int)Mathf.Log(input.width, 2); for (int i = 0; i <= maxLevels; i++) { float offset = 1 << (maxLevels - i); cmd.SetComputeFloatParam(computeShader, "_InputScaleFactor", 1); cmd.SetComputeFloatParam(computeShader, "_Offset", offset); MixtureUtils.SetTextureWithDimension(cmd, computeShader, jumpFloodingKernel, "_Input", output); MixtureUtils.SetTextureWithDimension(cmd, computeShader, jumpFloodingKernel, "_Output", rt); cmd.SetComputeIntParam(computeShader, "_DistanceMode", (int)distanceMode); DispatchCompute(cmd, jumpFloodingKernel, output.width, output.height, output.volumeDepth); TextureUtils.CopyTexture(cmd, rt, output); } cmd.SetComputeFloatParam(computeShader, "_InputScaleFactor", (float)input.width / (float)output.width); cmd.SetComputeIntParam(computeShader, "_DistanceMode", (int)distanceMode); MixtureUtils.SetTextureWithDimension(cmd, computeShader, finalPassKernel, "_Input", input); MixtureUtils.SetTextureWithDimension(cmd, computeShader, finalPassKernel, "_Output", rt); MixtureUtils.SetTextureWithDimension(cmd, computeShader, finalPassKernel, "_FinalOutput", output); DispatchCompute(cmd, finalPassKernel, output.width, output.height, output.volumeDepth); return(true); }
protected override bool ProcessNode(CommandBuffer cmd) { settings.doubleBuffered = true; if (!base.ProcessNode(cmd) || input == null) { return(false); } TextureUtils.CopyTexture(cmd, input, output, false); var mipmapGenMat = GetTempMaterial("Hidden/Mixture/GenerateMipMaps"); if (mode == Mode.Custom) { mipmapGenMat = material; } else { output.material = null; } if (mode == Mode.Auto) { cmd.GenerateMips(output); } else { var props = new MaterialPropertyBlock(); MixtureUtils.SetupDimensionKeyword(mipmapGenMat, settings.GetResolvedTextureDimension(graph)); mipmapGenMat.SetFloat("_Mode", (int)mode); MixtureUtils.SetTextureWithDimension(props, "_PreviousMip", input); // Manually generate mips: for (int i = 0; i < output.mipmapCount - 1; i++) { props.SetFloat("_SourceMip", i); float width = Mathf.Max(1, input.width >> i); float height = Mathf.Max(1, input.width >> i); float depth = Mathf.Max(1, TextureUtils.GetSliceCount(input) >> i); props.SetVector("_RcpTextureSize", new Vector4(1.0f / width, 1.0f / height, 1.0f / depth, 0.0f)); output.material = mipmapGenMat; if (mode == Mode.Gaussian) { // 2 passes of gaussian blur for 2D and Cubemaps props.SetVector("_GaussianBlurDirection", Vector3.right); CustomTextureManager.UpdateCustomRenderTexture(cmd, output, 1, i + 1, props); TextureUtils.CopyTexture(cmd, output.GetDoubleBufferRenderTexture(), output, i + 1); props.SetFloat("_SourceMip", i + 1); MixtureUtils.SetTextureWithDimension(props, "_PreviousMip", output); props.SetVector("_GaussianBlurDirection", Vector3.up); CustomTextureManager.UpdateCustomRenderTexture(cmd, output, 1, i + 1, props); // And a third pass if we're in 3D if (input.dimension == TextureDimension.Tex3D) { props.SetVector("_GaussianBlurDirection", Vector3.forward); TextureUtils.CopyTexture(cmd, output.GetDoubleBufferRenderTexture(), output, i + 1); CustomTextureManager.UpdateCustomRenderTexture(cmd, output, 1, i + 1, props); } } else { CustomTextureManager.UpdateCustomRenderTexture(cmd, output, 1, i + 1, props); } TextureUtils.CopyTexture(cmd, output.GetDoubleBufferRenderTexture(), output, i + 1); MixtureUtils.SetTextureWithDimension(props, "_PreviousMip", output); } } output.material = null; return(true); }