2019-05-29 【Unity】Terrain畫線03:投影Projector

經(jīng)實驗默怨,這種方式是目前能做到最好的方式了质和。



  • 原理:
  1. 在點擊的地方畫出LineRender,分別計算出各個點的最大、最小X羞迷,Y坐標(biāo),可以得到一個矩形距糖。
    image.png
  2. 獲得矩形長寬較大的一個撤蟆,作為Ortho相機的OrthoSize;同時也作為Projector投影器的OrthoSize牵舵。把相機和投影器放置在正確的位置上柒啤,正好能把畫的紅線罩住。


    image.png
  3. Ortho相機拍照一張畸颅,作為投影器的貼圖担巩。(增大RenderTexture貼圖分辨率,可以獲得更好的效果)投影器向下投影没炒。Over涛癌。

  • 部分代碼


    image.png
using SGF.Unity.Common;
using System;
using System.Collections.Generic;
using UnityEngine;


public enum DrawMode
{
    NULL,
    POLYGON,
    ELLIPTICAL,
}


public class TextureGenerator : MonoSingleton<TextureGenerator>
{
    private float _minX;
    private float _maxX;
    private float _minY;
    private float _maxY;

    private float _middleX;
    private float _middleY;
    private int _textureWidth;
    private int _textureHeight;

    public Color LineColor = Color.red; //作為單例,提供顏色給ProjController畫lineRender
    private Color _backgroundColor = new Color(1, 1, 1, 0); //白色透明

    [Header("生成貼圖分辨率倍數(shù)")]
    [Range(1,20)]
    public int TexResolutionMult = 10;
    [Header("投影器距離地面高度--要比最高的地面位置高")]
    public float ProjectorHeight = 200;

    public Vector3 GetProjectorPos()
    {
        return new Vector3(_middleX, ProjectorHeight, _middleY);
    }

    private void DataRelease()
    {
        _minX = 0;
        _maxX = 0;
        _minY = 0;
        _maxY = 0;
        _middleX = 0;
        _middleY = 0;
        _textureWidth = 0;
        _textureHeight = 0;
    }


    /// <summary>
    /// 根據(jù)相機拍照LineRender生成紋理圖片
    /// </summary>
    /// <param name="camera"></param>
    /// <param name="proj"></param>
    /// <param name="posList"></param>
    /// <param name="closePolygon"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    public Texture2D GenerateProceduralTexture(Camera camera, Projector proj, DrawMode type = DrawMode.POLYGON)
    {
        //CalculateTextureWH(posList);  //提前計算過了

        int longSide = _textureWidth > _textureHeight ? _textureWidth : _textureHeight;

        float orthoSize = longSide / 2.0f;
        camera.orthographicSize = orthoSize;
        proj.orthographicSize = orthoSize;

        longSide *= TexResolutionMult;  //乘以分辨率倍數(shù)

        RenderTexture rt = null;
        rt = camera.targetTexture;
        if (rt == null)
        {
            rt = new RenderTexture(longSide, longSide, 0);
            camera.targetTexture = rt;
        }
        else
        {
            rt.Release();
            rt.width = longSide;
            rt.height = longSide;
        }

        Texture2D image = new Texture2D(longSide, longSide);

        // The Render Texture in RenderTexture.active is the one
        // that will be read by ReadPixels.
        RenderTexture.active = rt;    //設(shè)置當(dāng)前活動的rendertexture為當(dāng)前相機的

        // Render the camera's view.
        camera.Render();

        // Make a new texture and read the active Render Texture into it.
        //image.ReadPixels(new Rect(0, 0, camera.targetTexture.width, camera.targetTexture.height), 0, 0);
        image.ReadPixels(new Rect(0, 0, longSide, longSide), 0, 0);
        image.Apply();

        return image;
    }

    #region ===== 程序生成紋理(方案已被取代) =====
    /// <summary>
    /// 根據(jù)計算點生成圖片
    /// </summary>
    /// <param name="posList"></param>
    /// <param name="closePolygon"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    //public Texture2D GenerateProceduralTexture(List<Vector3> posList, bool closePolygon = false, DrawMode type = DrawMode.POLYGON)
    //{
    //    CalculateTextureWH(posList);
    //    Debug.Log(_textureWidth + ", " + _textureHeight);
    //    Texture2D proceduralTexture = new Texture2D(_textureWidth, _textureHeight);

    //    for(int w = 0;w < _textureWidth; w ++)
    //    {
    //        for(int h = 0; h < _textureHeight; h++)
    //        {
    //            Color pixel = _backgroundColor;

    //            //遍歷該點距離每條邊的距離,一旦有一條邊滿足條件跳出循環(huán)
    //            for(int i = 0;i < posList.Count;i ++)
    //            {
    //                int next = i + 1;
    //                if (next == posList.Count) next = 0;

    //                Vector3 vstart = WorldToTexture(posList[i]);
    //                Vector3 vend = WorldToTexture(posList[next]);
    //                Vector3 vcurrent = new Vector3(w, 0, h);

    //                if (PointInRect(vstart, vend, vcurrent, LineWidth))
    //                {
    //                    if (PointToStraightlineDistance(vstart, vend, vcurrent) <= LineWidth)
    //                    {
    //                        pixel = LineColor;
    //                        break;
    //                    }
    //                }
    //            }

    //            proceduralTexture.SetPixel(w, h, pixel);
    //        }
    //    }

    //    proceduralTexture.Apply();
    //    return proceduralTexture;
    //}
    #endregion

    //計算圖片長寬
    public void CalculateTextureWH(List<Vector3> posList)
    {
        DataRelease();

        for (int i = 0;i < posList.Count;i ++)
        {
            float curX = posList[i].x;
            float curY = posList[i].z;

            if(i == 0)
            {
                _minX = _maxY = curX;
                _minY = _maxY = curY;
            }

            if (curX < _minX) _minX = curX;
            if (curX >= _maxX) _maxX = curX;
            if (curY < _minY) _minY = curY;
            if (curY >= _maxY) _maxY = curY;
        }

        _middleX = (_minX + _maxX) / 2;
        _middleY = (_minY + _maxY) / 2;
        _textureWidth = (int)Math.Ceiling(_maxX - _minX);
        _textureHeight = (int)Math.Ceiling(_maxY - _minY);
    }

    //轉(zhuǎn)換世界坐標(biāo)系到圖片坐標(biāo)系
    private Vector3 WorldToTexture(Vector3 originData)
    {
        Vector3 transData = new Vector3(originData.x - _minX, 0, originData.z - _minY);
        return transData;
    }


    /// <summary>
    /// 點到直線的距離
    /// </summary>
    /// <returns>The of point to vector.</returns>
    /// <param name="startPoint">Start point.</param>
    /// <param name="endPoint">End point.</param>
    /// <param name="point">Point.</param>
    public float PointToStraightlineDistance(Vector3 lineStartPoint, Vector3 lineEndPoint, Vector3 targetPoint)
    {
        //需要轉(zhuǎn)到2維平面計算
        Vector2 startVe2 = IgnoreYAxis(lineStartPoint);
        Vector2 endVe2 = IgnoreYAxis(lineEndPoint);
        float A = endVe2.y - startVe2.y;
        float B = startVe2.x - endVe2.x;
        float C = endVe2.x * startVe2.y - startVe2.x * endVe2.y;
        float denominator = Mathf.Sqrt(A * A + B * B);
        Vector2 pointVe2 = IgnoreYAxis(targetPoint);
        return Mathf.Abs((A * pointVe2.x + B * pointVe2.y + C) / denominator);
    }


    /// <summary>
    /// 只計算 起點到終點+linewidth  矩形內(nèi)的點拳话,忽略矩形外部的點
    /// </summary>
    /// <param name="lineStartPoint"></param>
    /// <param name="lineEndPoint"></param>
    /// <param name="targetPoint"></param>
    /// <returns></returns>
    private bool PointInRect(Vector3 lineStartPoint, Vector3 lineEndPoint, Vector3 targetPoint, float linewidth)
    {
        float minx,miny,maxx,maxy = 0;
        if(lineStartPoint.x < lineEndPoint.x)
        {
            minx = lineStartPoint.x;
            maxx = lineEndPoint.x;
        }
        else
        {
            minx = lineEndPoint.x;
            maxx = lineStartPoint.x;
        }

        if (lineStartPoint.y < lineEndPoint.y)
        {
            miny = lineStartPoint.y;
            maxy = lineEndPoint.y;
        }
        else
        {
            miny = lineEndPoint.y;
            maxy = lineStartPoint.y;
        }

        //minx -= linewidth;
        //maxx += linewidth;
        //miny -= linewidth;
        //maxy += linewidth;

        if (targetPoint.x <= maxx && targetPoint.x >= minx)
        {
            if(targetPoint.y <= maxy && targetPoint.y >= miny)
            {
                return true;
            }
        }

        return false;
    }


    /// <summary>
    /// 去掉三維向量的Y軸先匪,把向量投射到xz平面。
    /// </summary>
    /// <param name="vector3"></param>
    /// <returns></returns>
    public static Vector2 IgnoreYAxis(Vector3 vector3)
    {
        return new Vector2(vector3.x, vector3.z);
    }




}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弃衍,一起剝皮案震驚了整個濱河市呀非,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌镜盯,老刑警劉巖岸裙,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異速缆,居然都是意外死亡降允,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門激涤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拟糕,“玉大人,你說我怎么就攤上這事倦踢∷椭停” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵辱挥,是天一觀的道長犁嗅。 經(jīng)常有香客問我,道長晤碘,這世上最難降的妖魔是什么褂微? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮园爷,結(jié)果婚禮上宠蚂,老公的妹妹穿的比我還像新娘。我一直安慰自己童社,他們只是感情好求厕,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扰楼,像睡著了一般呀癣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弦赖,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天项栏,我揣著相機與錄音,去河邊找鬼蹬竖。 笑死沼沈,一個胖子當(dāng)著我的面吹牛流酬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播列另,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼康吵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了访递?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤同辣,失蹤者是張志新(化名)和其女友劉穎拷姿,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旱函,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡响巢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了棒妨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踪古。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖券腔,靈堂內(nèi)的尸體忽然破棺而出伏穆,到底是詐尸還是另有隱情,我是刑警寧澤纷纫,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布枕扫,位于F島的核電站,受9級特大地震影響辱魁,放射性物質(zhì)發(fā)生泄漏烟瞧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一染簇、第九天 我趴在偏房一處隱蔽的房頂上張望参滴。 院中可真熱鬧,春花似錦锻弓、人聲如沸砾赔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽过蹂。三九已至,卻和暖如春聚至,著一層夾襖步出監(jiān)牢的瞬間酷勺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工扳躬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留脆诉,地道東北人甚亭。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像击胜,于是被迫代替她去往敵國和親亏狰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

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