Unity渲染頂點作為顏色到紋理獲取頂點的世界坐標

通過渲染到浮點紋理實現(xiàn)三維對象拾取
Unity 瑣碎(2): Shader 顏色調(diào)試

最近項目有個新需求锚贱,在AR場景中,點擊場景的任意位置可以獲取到點擊位置物體表面的位置橄维,傳統(tǒng)的都是用碰撞來做训堆,給不同對象添加BoxCollider做葵,然后發(fā)射線护蝶,即可华烟。不過我們希望碰撞點的位置能在物體的表面,而不是包圍盒的表面持灰,打個比方盔夜,一把椅子,它的包圍盒中有很大一塊區(qū)域是空的堤魁,因為包圍盒的計算是將所有頂點都包含在盒子中喂链,所以碰撞點只能落在包圍盒上,看起來離椅子還有很遠的距離妥泉,如果使用mesh collider椭微,對于一個場景中任意位置都支持的話,模型太多盲链,性能消耗不起蝇率。


椅子包圍盒

后來經(jīng)群友提示,可以將頂點的位置(XYZ)作為顏色(RGB)渲染到RT上刽沾,再從RT上采樣獲取RGB值本慕,將RGB值轉換為世界坐標,這是一種非常取巧的方式侧漓,也可以說是歪門邪道间狂,哈哈,不過能實現(xiàn)功能就好火架。
下面說下核心的思路和代碼:
1.先將頂點位置通過shader轉換為世界坐標鉴象,注意世界坐標是(-∞,+∞),而顏色是[0,1]何鸡,所以需要做一個映射纺弊,先歸一化,再映射到[0,1]骡男,注意dis * 0.01淆游,作為A通道輸出,是用于獲取世界坐標的逆運算隔盛,乘以0.01是為了轉換[0,1]區(qū)間犹菱,我目前的項目中不會超過100單位,這是是個經(jīng)驗值吮炕,可以自行嘗試得到一個較好的值腊脱。

這個shader是在Camera的OnPreRender時使用RenderWithShader方法,臨時替代原shader 獲取一張RT時使用的龙亲,所以要注意陕凹,凡是需要渲染頂點到顏色的材質(zhì)使用過的RenderType悍抑,都要實現(xiàn)一遍,關于RenderType可以參考筆者之前的一篇文章UnityShader RenderType杜耙。因為筆者的場景中材質(zhì)shader使用到三種RenderType搜骡,分別是:

    Tags { "RenderType"="Opaque" }

    Tags { "RenderType"="TransparentCutout" }

    Tags { "RenderType"="Transparent" }

所以需要3個SubShader,Tags分別標記為上述三個類別佑女。

SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 300

    Pass {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #pragma target 2.0

        #include "UnityCG.cginc"

        struct appdata_t {
            float4 vertex : POSITION;
            float4 uv: TEXCOORD0;
        };

        struct v2f {
            float4 vertex : SV_POSITION;
            float2 uv: TEXCOORD0; 
            float4 worldPos : TEXCOORD1;
        };

        float4 _MainTex_TexelSize;

        v2f vert (appdata_t v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            //需要處理UV翻轉問題
        #if UNITY_UV_STARTS_AT_TOP 
            if(_MainTex_TexelSize.y < 0)
                o.uv = float2(v.uv.x, 1-v.uv.y);
            else
                o.uv = v.uv;
        #else
            o.uv = v.uv;
        #endif
            o.worldPos =  mul(unity_ObjectToWorld, v.vertex);
            return o;
        }

        float4 frag (v2f i) : SV_Target
        {
            //世界坐標映射到顏色
            float dis = length(i.worldPos.xyz);
            float3 worldPos2 = i.worldPos.xyz/dis;
            worldPos2 = worldPos2 * 0.5 + 0.5;
            return float4(worldPos2,dis * 0.01);
        }
        ENDCG
    }

SubShader {
    Tags { "RenderType"="TransparentCutout" }
    ...
    }

SubShader {
    Tags { "RenderType"="Transparent" }
    ...
    }
}

保存該shader记靡。接下來需要在Camera的OnPreRender中使用該shader替換得到一張RT。

public Camera depthCam;
private RenderTexture depthTexture;
private Texture2D texture2D;

private void OnPreRender()
{
    if (depthCam == null) return;
    if (depthTexture)
     {
        RenderTexture.ReleaseTemporary(depthTexture);
        depthTexture = null;
     }
    depthCam.CopyFrom(Camera.main);
    depthTexture = RenderTexture.GetTemporary(Camera.main.pixelWidth, Camera.main.pixelHeight, 32, RenderTextureFormat.ARGB32);
    depthCam.backgroundColor = new Color(0, 0, 0, 0);
    depthCam.clearFlags = CameraClearFlags.SolidColor;
    depthCam.targetTexture = depthTexture;
    depthCam.RenderWithShader(shader, "RenderType");//替換shader团驱,獲取rt

    int width = depthTexture.width;
    int height = depthTexture.height;
    texture2D = new Texture2D(width, height, TextureFormat.ARGB32, false);//屏幕中心的顏色
    RenderTexture temp = RenderTexture.active;
    RenderTexture.active = depthTexture;
    texture2D.ReadPixels(new Rect(0, 0, width, height), 0, 0);
    texture2D.Apply();
    RenderTexture.active = temp;
    Color color = texture2D.GetPixel(width / 2, height / 2);//這里采樣為中心點
   //逆運算得到世界坐標
    Vector3 w = new Vector3(color.r, color.g, color.b);
    float l = color.a * 100f;
    w.x = (w.x - 0.5f) * 2 * l;
    w.y = (w.y - 0.5f) * 2 * l;
    w.z = (w.z - 0.5f) * 2 * l;
    Debug.Log(w);
}
頂點作為顏色輸出得到的RenderTexture
Editor下截圖

最后輸出的就是屏幕中心頂點的世界坐標摸吠,當然還可以改成鼠標點擊的位置。

小結

使用該方法獲取到的世界坐標的位置并不是非常準確店茶,大部分時候都是正確的蜕便,但是有時候會有一點偏差劫恒,筆者猜測是精度導致的問題贩幻,目前還沒有確定是哪里導致的。另外對于使用了法線貼圖两嘴、視差貼圖或者其他在shader中導致視覺位置改變的材質(zhì)丛楚,一樣會產(chǎn)生偏移,這個也是要注意的憔辫。因為筆者項目的原因趣些,有一點偏差,最后再通過手動微調(diào)也是可以接受的贰您。如果哪位大佬還有更好的方式獲取屏幕頂點坏平,也請留言告知,不勝感激锦亦。
最后給出github的地址https://github.com/eangulee/Color2Pos舶替。
好了,準備下班了杠园。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末顾瞪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子抛蚁,更是在濱河造成了極大的恐慌陈醒,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞧甩,死亡現(xiàn)場離奇詭異钉跷,居然都是意外死亡,警方通過查閱死者的電腦和手機肚逸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門尘应,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惶凝,“玉大人,你說我怎么就攤上這事犬钢〔韵剩” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵玷犹,是天一觀的道長混滔。 經(jīng)常有香客問我,道長歹颓,這世上最難降的妖魔是什么坯屿? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮巍扛,結果婚禮上领跛,老公的妹妹穿的比我還像新娘。我一直安慰自己撤奸,他們只是感情好吠昭,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著胧瓜,像睡著了一般矢棚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上府喳,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天蒲肋,我揣著相機與錄音,去河邊找鬼钝满。 笑死兜粘,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的弯蚜。 我是一名探鬼主播孔轴,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼熟吏!你這毒婦竟也來了距糖?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤牵寺,失蹤者是張志新(化名)和其女友劉穎悍引,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帽氓,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡趣斤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了黎休。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浓领。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡玉凯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出联贩,到底是詐尸還是另有隱情漫仆,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布泪幌,位于F島的核電站盲厌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏祸泪。R本人自食惡果不足惜吗浩,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望没隘。 院中可真熱鬧懂扼,春花似錦、人聲如沸右蒲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽品嚣。三九已至炕倘,卻和暖如春钧大,著一層夾襖步出監(jiān)牢的瞬間翰撑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工啊央, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留眶诈,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓瓜饥,卻偏偏與公主長得像逝撬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子乓土,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

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