Skip to content

Instantly share code, notes, and snippets.

@HAliss
Created March 6, 2019 10:25
Show Gist options
  • Save HAliss/cc79aa4d3e0b616f1ce75122ad9c36fc to your computer and use it in GitHub Desktop.
Save HAliss/cc79aa4d3e0b616f1ce75122ad9c36fc to your computer and use it in GitHub Desktop.
Texture combiner tool that uses a shader to combine different textures to specific color channels.
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
public class ShaderTextureCombiner : EditorWindow {
//Input textures
private Texture2D[] textures = new Texture2D[4];
//Invert bool for each texture
private bool[] invert = new bool[4];
//Output texture
private Texture2D generatedTexture;
//Dimensions of output texture
private Vector2Int textureDimensions;
//The value for the channels where a texture is not provided
private float defaultValue = 1.0f;
//Counter of generated textures used for naming
private int totalTextures;
private bool hasAlpha = false;
[MenuItem("Tools/ShaderTextureCombiner")]
private static void ShowWindow() {
var window = GetWindow<ShaderTextureCombiner>();
window.titleContent = new GUIContent("Texture Combiner");
window.Show();
}
private void OnGUI() {
//Displaying the texture fields
textures[0] = (Texture2D) EditorGUILayout.ObjectField("Texture 1 (R)", textures[0], typeof(Texture2D), false);
invert[0] = GUILayout.Toggle(invert[0], "Invert", "Button");
textures[1] = (Texture2D) EditorGUILayout.ObjectField("Texture 2 (G)", textures[1], typeof(Texture2D), false);
invert[1] = GUILayout.Toggle(invert[1], "Invert", "Button");
textures[2] = (Texture2D) EditorGUILayout.ObjectField("Texture 3 (B)", textures[2], typeof(Texture2D), false);
invert[2] = GUILayout.Toggle(invert[2], "Invert", "Button");
textures[3] = (Texture2D) EditorGUILayout.ObjectField("Texture 4 (A)", textures[3], typeof(Texture2D), false);
invert[3] = GUILayout.Toggle(invert[3], "Invert", "Button");
//Displaying the texture information
textureDimensions = EditorGUILayout.Vector2IntField("Dimensions", textureDimensions);
defaultValue = EditorGUILayout.Slider("Default value", defaultValue, 0.0f, 1.0f);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Generate texture")) {
GenerateTexture();
}
if (GUILayout.Button("Save texture")) {
SaveGeneratedTexture();
}
GUILayout.EndHorizontal();
//Showing preview of the generated texture and its alpha (if it has any)
if (generatedTexture != null) {
EditorGUILayout.LabelField("Generated texture preview");
EditorGUI.DrawPreviewTexture(new Rect(50, 450, 100, 100), generatedTexture);
if (hasAlpha) {
EditorGUI.DrawTextureAlpha(new Rect(200, 450, 100, 100), generatedTexture);
}
}
}
private void GenerateTexture() {
generatedTexture = null;
if (AllTexturesAreEmpty()) {
Debug.LogWarning("No input textures given, not generating a texture!");
return;
}
hasAlpha = (textures[3] != null);
RenderTexture rt = new RenderTexture(textureDimensions.x, textureDimensions.y, 0, GraphicsFormat.R8G8B8A8_SRGB);
RenderTexture.active = rt;
//Creating a material based on the custom shader
Material mat = new Material(Shader.Find("Hidden/TextureCombinerShader"));
SetMaterialProperties(mat);
//Rendering on the render texture using the custom material
Graphics.Blit(null, rt, mat);
generatedTexture = new Texture2D(textureDimensions.x, textureDimensions.y, hasAlpha ? TextureFormat.RGBA32 : TextureFormat.RGB24, false);
generatedTexture.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
generatedTexture.Apply();
}
private void SetMaterialProperties(Material mat) {
mat.SetTexture("_TextureR", textures[0]);
mat.SetTexture("_TextureG", textures[1]);
mat.SetTexture("_TextureB", textures[2]);
mat.SetTexture("_TextureA", textures[3]);
mat.SetFloat("_DefaultValue", defaultValue);
mat.SetVector("_TexturesGiven", GetTexturesGiven());
mat.SetVector("_InvertedTextures", GetInvertedTextures());
}
private Vector4 GetInvertedTextures() {
Vector4 invertedTextures = new Vector4();
invertedTextures.x = (invert[0]) ? 1 : 0;
invertedTextures.y = (invert[1]) ? 1 : 0;
invertedTextures.z = (invert[2]) ? 1 : 0;
invertedTextures.w = (invert[3]) ? 1 : 0;
return invertedTextures;
}
private Vector4 GetTexturesGiven() {
Vector4 texturesGiven = new Vector4();
texturesGiven.x = (textures[0] == null) ? 0 : 1;
texturesGiven.y = (textures[1] == null) ? 0 : 1;
texturesGiven.z = (textures[2] == null) ? 0 : 1;
texturesGiven.w = (textures[3] == null) ? 0 : 1;
return texturesGiven;
}
private void SaveGeneratedTexture() {
GenerateTexture();
//If a directory called "Textures/Generated Textures" doesn't exist, create it
if (!Directory.Exists(Application.dataPath + "Textures/Generated Textures")) {
Directory.CreateDirectory(Application.dataPath + "/Textures/Generated Textures/");
totalTextures = 0;
} else {
totalTextures = Directory.GetFiles(Application.dataPath + "/Textures/Generated Textures/").Length;
}
byte[] bytes = generatedTexture.EncodeToPNG();
while (File.Exists(Application.dataPath + "/Textures/Generated Textures/generated_texture_" + totalTextures.ToString() + ".png")) {
totalTextures++;
}
File.WriteAllBytes(Application.dataPath + "/Textures/Generated Textures/generated_texture_" + totalTextures.ToString() + ".png", bytes);
AssetDatabase.Refresh();
EditorUtility.FocusProjectWindow();
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath("Assets/Textures/Generated Textures/generated_texture_" + totalTextures.ToString() + ".png");
}
private bool AllTexturesAreEmpty() {
for (int i = 0; i < 4; i++) {
if (textures[i] != null) {
return false;
}
}
return true;
}
}
Shader "Hidden/TextureCombinerShader"
{
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _TextureR;
sampler2D _TextureG;
sampler2D _TextureB;
sampler2D _TextureA;
half4 _TexturesGiven;
half4 _InvertedTextures;
float _DefaultValue;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the textures
fixed4 colR = tex2D(_TextureR, i.uv);
fixed4 colG = tex2D(_TextureG, i.uv);
fixed4 colB = tex2D(_TextureB, i.uv);
fixed4 colA = tex2D(_TextureA, i.uv);
fixed R = abs(_InvertedTextures.x - colR.r * colR.a);
fixed G = abs(_InvertedTextures.y - colG.r * colG.a);
fixed B = abs(_InvertedTextures.z - colB.r * colB.a);
fixed A = abs(_InvertedTextures.w - colA.r * colA.a);
fixed4 texCol = fixed4(R,G,B,A);
fixed4 col = lerp(texCol, _DefaultValue , (1 - _TexturesGiven));
return col;
}
ENDCG
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment