unity3D 中實現(xiàn)在模型上繪制紋理

簡介

根據(jù)個人需要,想在unity3D中實現(xiàn)在3D模型上繪制紋理的功能椎扬,經(jīng)過多方查找資料和一些個人的思考颅停,寫了一個粗略的版本出來谓晌,在此總結(jié)一點(diǎn)個人的淺見,方便后時遺忘時癞揉,有址可尋纸肉,由于個人經(jīng)驗善淺,如若存在不足喊熟,歡迎大佬指正柏肪。

實現(xiàn)

<pre>
using UnityEngine;
using System.Collections;

// 由于想做紋理的旋轉(zhuǎn)操作,在unity3D中沒有找到3階矩陣芥牌,所以自己動手寫了一個烦味,也算是自己動手豐衣足食。
class Matrix3x3
{
float m11, m21, m31,
m12, m22, m32,
m13, m23, m33;
public Matrix3x3()
{
Identity();
}

//  單位化矩陣
void Identity()
{
    m11 = 1.0f; m21 = 0.0f; m31 = 0.0f;
    m12 = 0.0f; m22 = 1.0f; m32 = 0.0f;
    m13 = 0.0f; m23 = 0.0f; m33 = 1.0f;
}
// 平移矩陣
public void Translation(float x, float y)
{
    m11 = 1.0f; m21 = 0.0f; m31 = 0.0f;
    m12 = 0.0f; m22 = 1.0f; m32 = 0.0f;
    m13 = x; m23 = y; m33 = 1.0f;
}
//  旋轉(zhuǎn)矩陣
public void Rotation(float a)
{
    m11 = Mathf.Cos(a); m21 = Mathf.Sin(a); m31 = 0.0f;
    m12 = -m21; m22 = m11; m32 = 0.0f;
    m13 = 0.0f; m23 = 0.0f; m33 = 1.0f;
}

//  縮放矩陣
public void Scale(float x, float y)
{
    m11 = x; m21 = 0.0f; m31 = 0.0f;
    m12 = 0.0f; m22 = y; m32 = 0.0f;
    m13 = 0.0f; m23 = 0.0f; m33 = 1.0f;
}

// 矩陣乘二位向量
public Vector2 MxV(Vector2 v)
{
Vector3 sv = new Vector3(v.x, v.y, 1);
Vector3 rv;
rv.x = sv.x * m11 + sv.y * m12 + sv.z * m13;
rv.y = sv.x * m21 + sv.y * m22 + sv.z * m23;
rv.z = sv.x * m31 + sv.y * m32 + sv.z * m33;
return new Vector2(rv.x / rv.z, rv.y / rv.z);
}
}

public class Wound : MonoBehaviour
{
// 筆刷貼圖(選擇比較小的壁拉,在取像素是會較為方便)
public Texture2D brush_Texture;
// 傷口模型的碰撞層級(和其他的模型區(qū)分開來谬俄,防止誤操作)
public LayerMask woundMask;
// 傷口模型的初始化貼圖(初始化的時候賦予傷口模型,確保傷口模型完全透明)
public Texture2D saveTexture;

//  光標(biāo)位置
Vector2 cursPos;
//  紋理坐標(biāo)的上方向
Vector2 text_UP = new Vector2(0, -1);
//  紋理矩陣
Matrix3x3 my_Matrix = new Matrix3x3();

//  筆刷半徑
public float brush_Radius = 8;
//  筆刷流量(此處使用了時間做為間隔的判斷弃理,主要是個人對于筆刷流量的具體控制形勢了解的不甚明朗^_^)
public float flow = 0.2f;
float count;

//  傷口模型材質(zhì)的原始貼圖
Texture2D oring_Texture = null;
//  繪制傷口的貼圖
Texture2D editTexture = null;
//  前一次紋理采樣的uv位置
Vector2 last_UV;
//  當(dāng)前紋理采樣的uv位置
Vector2 current_UV;
//  UV的比例值
float u_per_Pixel = -1;
float v_per_Pixel = -1;
//  brush紋理采樣
float sample_W;
float sample_H;

// Use this for initialization
void Start()
{
    count = 0;
    //  初始化上一次采樣的uv位置
    last_UV = new Vector2(-1, -1);
    //  初始化當(dāng)前采樣的uv位置
    current_UV = new Vector2(-1, -1);
    //  根據(jù)筆刷半徑和筆刷紋理大小計算brush紋理采樣值
    sample_W = brush_Texture.width / (2 * brush_Radius);
    sample_H = brush_Texture.height / (2 * brush_Radius);
}

// Update is called once per frame
void Update()
{
}

// 繪制傷口
public void DrawTexture(Vector2 cursorPos)
{
    if(count > 0)
    {
        count -= Time.deltaTime;
        return;
    }
    count = flow;
    //  射線碰撞檢測
    Ray myRay = Camera.main.ScreenPointToRay(cursorPos);
    RaycastHit myHit;
    if (Physics.Raycast(myRay, out myHit, 1000f, woundMask))
    {
        //  普通模型
        MeshRenderer myMeshRender = myHit.transform.GetComponent<MeshRenderer>();
        if (myMeshRender)
        {
            // 如果當(dāng)前原始圖像為空
            if (oring_Texture == null)
            {
                //  獲取原始圖像
                oring_Texture = myMeshRender.sharedMaterial.mainTexture as Texture2D;
                //  如果當(dāng)前模型的材質(zhì)沒有貼圖溃论,則創(chuàng)建一張1024*1024的圖像,
                if (oring_Texture == null)
                {
                    u_per_Pixel = 1.0f / 1024;
                    v_per_Pixel = 1.0f / 1024;
                    editTexture = new Texture2D(1024, 1024, TextureFormat.ARGB32, false);
                    //  設(shè)置貼圖為全白透明
                    Color cT = new Color(1, 1, 1, 0);
                    for (int y = 0; y < 1024; ++y)
                    {
                        for (int x = 0; x < 1024; ++x)
                        {
                            editTexture.SetPixel(x, y, cT);
                        }
                    }
                }
                //  如果有原始圖像
                else
                {
                    //  計算uv的比重
                    u_per_Pixel = 1.0f / oring_Texture.width;
                    v_per_Pixel = 1.0f / oring_Texture.height;
                    //  根據(jù)原始貼圖案铺,創(chuàng)建新的紋理
                    editTexture = new Texture2D(oring_Texture.width, oring_Texture.height, TextureFormat.ARGB32, false);
                    editTexture.alphaIsTransparency = true;
                    //  將原始圖像的像素賦予給新圖片
                    for (int y = 0; y < oring_Texture.height; ++y)
                    {
                        for (int x = 0; x < oring_Texture.width; ++x)
                        {
                            editTexture.SetPixel(x, y, oring_Texture.GetPixel(x, y));
                        }
                    }
                    //  更新傷口繪制貼圖
                    editTexture.Apply();
                }
                //  使用可編輯的新圖替換材質(zhì)
                myMeshRender.sharedMaterial.mainTexture = editTexture;
            }
            //  當(dāng)前光標(biāo)點(diǎn)對應(yīng)的uv坐標(biāo)
            current_UV = myHit.textureCoord;
            //  如果是第一次進(jìn)入,更新上一次的uv采樣為當(dāng)前采樣
            if (last_UV.x < 0 || last_UV.y < 0)
            {
                last_UV = current_UV;
            }
            //  如果不是第一次進(jìn)入
            else if (last_UV.x >= 0 && last_UV.y >= 0)
            {
                //  如果當(dāng)前采樣和前一次采樣相同梆靖,
                if (current_UV.x == last_UV.x && current_UV.y == last_UV.y)
                    return;
                //  當(dāng)前朝向
                Vector2 current_Dir = current_UV - last_UV;
                //  圖片的旋轉(zhuǎn)量
                float angle = Vector2.Angle(text_UP, current_Dir);
                //  左旋或者右旋
                if (current_UV.x < last_UV.x)
                    angle *= -1;
                //  繪制筆刷范圍的像素
                DrawArea(current_UV, angle);
                //  更新前一次采樣為當(dāng)前采樣
                last_UV = current_UV;
                editTexture.Apply();
            }
        }
        //  蒙皮模型
        else
        {
            SkinnedMeshRenderer mySkinRender = myHit.transform.GetComponent<SkinnedMeshRenderer>();
            if (mySkinRender)
            {
                // 如果當(dāng)前原始圖像為空(即第一次對當(dāng)前模型進(jìn)行繪制)
                if (oring_Texture == null)
                {
                    //  獲取原始圖像
                    oring_Texture = mySkinRender.sharedMaterial.mainTexture as Texture2D;
                    //  如果當(dāng)前模型的材質(zhì)沒有貼圖控汉,則創(chuàng)建一張1024*1024的圖像,
                    if (oring_Texture == null)
                    {
                        u_per_Pixel = 1.0f / 1024;
                        v_per_Pixel = 1.0f / 1024;
                        editTexture = new Texture2D(1024, 1024, TextureFormat.ARGB32, false);
                        //  設(shè)置貼圖為全白透明
                        Color cT = new Color(1, 1, 1, 0);
                        for (int y = 0; y < 1024; ++y)
                        {
                            for (int x = 0; x < 1024; ++x)
                            {
                                editTexture.SetPixel(x, y, cT);
                            }
                        }
                    }
                    //  如果有原始圖像
                    else
                    {
                        //  計算uv的比重
                        u_per_Pixel = 1.0f / oring_Texture.width;
                        v_per_Pixel = 1.0f / oring_Texture.height;
                        //  根據(jù)原始貼圖返吻,創(chuàng)建新的紋理
                        editTexture = new Texture2D(oring_Texture.width, oring_Texture.height, TextureFormat.ARGB32, false);
                        //  將原始圖像的像素賦予給新圖片
                        for (int y = 0; y < oring_Texture.height; ++y)
                        {
                            for (int x = 0; x < oring_Texture.width; ++x)
                            {
                                editTexture.SetPixel(x, y, oring_Texture.GetPixel(x, y));
                            }
                        }
                        //  更新傷口繪制貼圖
                        editTexture.Apply();
                    }
                    //  使用可編輯的新圖替換材質(zhì)
                    mySkinRender.sharedMaterial.mainTexture = editTexture;
                }
                //  當(dāng)前光標(biāo)點(diǎn)對應(yīng)的uv坐標(biāo)
                current_UV = myHit.textureCoord;
                //  如果是第一次進(jìn)入姑子,更新上一次的uv采樣為當(dāng)前采樣
                if (last_UV.x < 0 || last_UV.y < 0)
                {
                    last_UV = current_UV;
                }
                //  如果不是第一次進(jìn)入
                else if (last_UV.x >= 0 && last_UV.y >= 0)
                {
                    //  如果當(dāng)前采樣和前一次采樣相同,
                    if (current_UV.x == last_UV.x && current_UV.y == last_UV.y)
                        return;
                    //  當(dāng)前朝向
                    Vector2 current_Dir = current_UV - last_UV;
                    //  圖片的旋轉(zhuǎn)量
                    float angle = Vector2.Angle(text_UP, current_Dir);
                    //  左旋或者右旋
                    if (current_UV.x < last_UV.x)
                        angle *= -1;
                    //  繪制筆刷范圍的像素
                    DrawArea(current_UV, angle);
                    //  更新前一次采樣為當(dāng)前采樣
                    last_UV = current_UV;
                    editTexture.Apply();
                }
            }
        }
    }
}

//  繪制筆刷區(qū)域
void DrawArea(Vector2 currentUV, float a)
{
    // 設(shè)置旋轉(zhuǎn)
    my_Matrix.Rotation(a * Mathf.Deg2Rad);
    //  當(dāng)前uv點(diǎn)在圖片中的像素位置
    int myPixle_X = Mathf.FloorToInt(currentUV.x * editTexture.width);
    int myPixle_Y = Mathf.FloorToInt(currentUV.y * editTexture.height);
    //  繪制
    int r = Mathf.FloorToInt(brush_Radius);
    //  初始化筆刷紋理采樣位置
    Vector2 brush_Sample = Vector2.zero;
    for (int y = -r; y < r; ++y)
    {
        //  筆刷紋理X方向的采樣回到本行開頭
        brush_Sample.x = 0;
        for(int x = -r; x < r; ++x)
        {
            //  當(dāng)前像素位置
            Vector2 cur_Pix = new Vector2(x, y);
            //  旋轉(zhuǎn)當(dāng)前像素
            Vector2 rot_Pix = my_Matrix.MxV(cur_Pix);
            //  平移像素
            rot_Pix.x += myPixle_X;
            rot_Pix.y += myPixle_Y;
            //  繪制當(dāng)前像素
            DrawSinglePixel(rot_Pix, brush_Sample);
            brush_Sample.x += sample_W;
        }
        brush_Sample.y += sample_H;
    }
}

//  繪制單個像素數(shù)
void DrawSinglePixel(Vector2 destPos,Vector2 brushPixelPos)
{
    //  計算當(dāng)前uv點(diǎn)在像素中的位置
    int destlx = Mathf.FloorToInt(destPos.x);
    int destly = Mathf.FloorToInt(destPos.y);
    int brushlx = Mathf.FloorToInt(brushPixelPos.x);
    int brushly = Mathf.FloorToInt(brushPixelPos.y);
    //  獲取背景色的顏色测僵,和筆刷的顏色
    Color brush_c = brush_Texture.GetPixel(brushlx, brushly);
    Color edit_c = editTexture.GetPixel(destlx, destly);
    //  根據(jù)筆刷的透明度街佑,計算新的填充色

    Color new_c;
    if (edit_c.a != 0)
    {
        new_c = new Color((1 - brush_c.a) * edit_c.r + brush_c.a * brush_c.r,
                                (1 - brush_c.a) * edit_c.g + brush_c.a * brush_c.g,
                                (1 - brush_c.a) * edit_c.b + brush_c.a * brush_c.b);
    }
    else
    {
        new_c = brush_c;
    }
    //  設(shè)置圖片
    editTexture.SetPixel(destlx, destly, new_c);
}

//  結(jié)束繪制
public void DrawEnd()
{
    last_UV = new Vector2(-1, -1);
    current_UV = new Vector2(-1, -1);
}

</pre>

附注

editTexture.alphaIsTransparency = true; 在unity編輯階段沒有問題谢翎,編譯的時候會報錯,所以在設(shè)置texture2D透明的時候沐旨,直接在創(chuàng)建的時候設(shè)置森逮,使用如下語句
editTexture = new Texture2D(oring_Texture.width, oring_Texture.height, TextureFormat.ARGB32 | TextureFormat.Alpha8, false);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市磁携,隨后出現(xiàn)的幾起案子褒侧,更是在濱河造成了極大的恐慌,老刑警劉巖谊迄,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闷供,死亡現(xiàn)場離奇詭異,居然都是意外死亡统诺,警方通過查閱死者的電腦和手機(jī)歪脏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粮呢,“玉大人婿失,你說我怎么就攤上這事」砑” “怎么了移怯?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長这难。 經(jīng)常有香客問我舟误,道長,這世上最難降的妖魔是什么姻乓? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任嵌溢,我火速辦了婚禮,結(jié)果婚禮上蹋岩,老公的妹妹穿的比我還像新娘赖草。我一直安慰自己,他們只是感情好剪个,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布秧骑。 她就那樣靜靜地躺著,像睡著了一般扣囊。 火紅的嫁衣襯著肌膚如雪乎折。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天侵歇,我揣著相機(jī)與錄音骂澄,去河邊找鬼。 笑死惕虑,一個胖子當(dāng)著我的面吹牛坟冲,可吹牛的內(nèi)容都是我干的磨镶。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼健提,長吁一口氣:“原來是場噩夢啊……” “哼琳猫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起矩桂,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤沸移,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后侄榴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雹锣,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年癞蚕,在試婚紗的時候發(fā)現(xiàn)自己被綠了蕊爵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡桦山,死狀恐怖攒射,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恒水,我是刑警寧澤会放,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站钉凌,受9級特大地震影響咧最,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜御雕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一矢沿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酸纲,春花似錦捣鲸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至疾嗅,卻和暖如春外厂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宪迟。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工酣衷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留交惯,地道東北人次泽。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓穿仪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親意荤。 傳聞我的和親對象是個殘疾皇子啊片,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容