캐릭터 합성 시 새로운 캐릭터를 생성해내기 위해 런타임에 수행될 텍스처 믹스 기능이 필요했다.
여기 예제에서는 프로젝트에서 쓰는 리소스 대신 무료 에셋을 이용했다
색깔을 아무거나 설정했더니 색조합이 구리당,,
이 기능을 위해 다음과 같이 목표와 구현하고자 하는 기능을 리스트업하고 하나씩 진행했다
목표
- 레퍼런스 텍스처들에서 몸통과 글로브 부분의 색깔을 랜덤으로 추출
- 추출된 색이 적용된 새로운 텍스처 생성
- 생성된 텍스처를 메쉬에 적용
public Color GetPixel(int x, int y);
을 이용했다.color.a == 0
인지 확인했다.public void SetPixel(int x, int y, Color color);
을 이용해서 설정해주었다.ex. monsterMainMesh.material.SetTexture("_BaseMap", resultTex);
// 쉐이더 프로퍼티 출력
string[] shaderPropertyTypes = new string[] { "Color", "Vector", "Float", "Range", "Texture" };
int propertyCount = ShaderUtil.GetPropertyCount(monsterMainMesh.material.shader);
for (int index = 0; index < propertyCount; ++index)
{
Debug.Log(ShaderUtil.GetPropertyName(monsterMainMesh.material.shader, index) + " "
+ shaderPropertyTypes[(int)ShaderUtil.GetPropertyType(monsterMainMesh.material.shader, index)]);
}
Read/Write Enabled
가 true 여야 한다using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class TextureMix : MonoBehaviour
{
public Button mixTextureBtn;
public Texture2D[] parentTextures;
public Texture2D alphaMixTexture1;
public Texture2D alphaMixTexture2;
public RawImage rawImagePart1;
public RawImage rawImagePart2;
public RawImage rawImageResult;
public Renderer monsterMainMesh;
public Renderer monsterPartMesh;
private string directoryPath = "Assets/Resources/NewTextures/";
public string fileName = "";
void Start()
{
mixTextureBtn.onClick.AddListener(() =>
{
// 포맷이 될 텍스처 선정
Texture2D formatTexture = parentTextures[0];
// 믹스용 텍스처에서 픽셀을 전부 가져옴
Color[] sourcePixels = alphaMixTexture1.GetPixels();
// --------------------- Part 1 --------------------------------
// - 레퍼런스 텍스처 중 하나 선정
int randomIdx = UnityEngine.Random.Range(0, parentTextures.Length);
Texture2D parentTexture = parentTextures[randomIdx];
// - 몬스터에 적용시킬 resultTex 생성
Texture2D resultTex = new Texture2D(formatTexture.width, formatTexture.height, TextureFormat.ARGB32, false);
// - 선정된 부분 텍스처 확인을 위한 partTex_1 생성
Texture2D partTex_1 = new Texture2D(formatTexture.width, formatTexture.height, TextureFormat.ARGB32, false);
// - 믹스용 텍스처의 pixel을 조회하면서 Alpha값이 0인 부분이 나오면, parentTexture에서 그 부분의 색깔을 가져다가 입힘
for (int h = 0; h < parentTexture.height; h++)
{
for (int w = 0; w < parentTexture.width; w++)
{
Color color = sourcePixels[h * formatTexture.width + w];
// 부모의 컬러값을 받아옴
Color parentColor = parentTexture.GetPixel(w, h);
if (color.a == 0)
{
partTex_1.SetPixel(w, h, parentColor); // 이 함수는 ARGB32, RGB24 와 Alpha8 텍스쳐 포맷에서만 작동
}
resultTex.SetPixel(w, h, parentColor);
}
}
partTex_1.Apply();
resultTex.Apply();
rawImagePart1.texture = partTex_1;
// --------------------- Part 2 --------------------------------
randomIdx = UnityEngine.Random.Range(0, parentTextures.Length);
parentTexture = parentTextures[randomIdx];
Texture2D partTex_2 = new Texture2D(formatTexture.width, formatTexture.height, TextureFormat.ARGB32, false);
sourcePixels = alphaMixTexture2.GetPixels();
for (int h = 0; h < formatTexture.height; h++)
{
for (int w = 0; w < formatTexture.width; w++)
{
Color color = sourcePixels[h * formatTexture.width + w];
Color parentColor = parentTexture.GetPixel(w, h);
if (color.a == 0)
{
partTex_2.SetPixel(w, h, parentColor);
resultTex.SetPixel(w, h, parentColor);
}
}
}
partTex_2.Apply();
resultTex.Apply();
rawImagePart2.texture = partTex_2;
rawImageResult.texture = resultTex;
monsterMainMesh.material.SetTexture("_BaseMap", resultTex);
monsterPartMesh.material.SetTexture("_BaseMap", resultTex);
// 새로 생성된 텍스처를 로컬에 저장
SaveTexture2DToPNGFile(resultTex, directoryPath, fileName);
});
}
private void SaveTexture2DToPNGFile(Texture2D texture, string directoryPath, string fileName)
{
if(false == Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
byte[] texturePNGBytes = texture.EncodeToPNG();
string filePath = directoryPath + fileName + ".png";
File.WriteAllBytes(filePath, texturePNGBytes);
}
}
오 잘 보고 갑니다!