2019-05-29 【Unity】Terrain画线03:投影Projector

经实验,这种方式是目前能做到最好的方式了。



  • 原理:
  1. 在点击的地方画出LineRender,分别计算出各个点的最大、最小X,Y坐标,可以得到一个矩形。
    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("生成贴图分辨率倍数")]
    [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>
    /// 根据相机拍照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;  //乘以分辨率倍数

        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;    //设置当前活动的rendertexture为当前相机的

        // 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>
    /// 根据计算点生成图片
    /// </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;

    //            //遍历该点距离每条边的距离,一旦有一条边满足条件跳出循环
    //            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);
    }

    //转换世界坐标系到图片坐标系
    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)
    {
        //需要转到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  矩形内的点,忽略矩形外部的点
    /// </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);
    }




}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容