1. 程式人生 > >Unity3D中的線性插值Lerp()函式解析

Unity3D中的線性插值Lerp()函式解析

unity3d中經常用線性插值函式Lerp()來在兩者之間插值,兩者之間可以是兩個材質之間、兩個向量之間、兩個浮點數之間、兩個顏色之間,其函式原型如下:

function Lerp(start : Material, end : Material, t : float) : void

在兩個材質之間插值

static functionLerp (from : Vector2, to : Vector2, t : float) : Vector2

兩個向量之間的線性插值。按照數字t在form到to之間插值。

t是夾在0到1之間。當t=0時,返回from。當t=1時,返回to。當t=0.5時放回from和to之間的平均數。

static functionLerp (from : Vector3, to :Vector3, t : float) :Vector3

兩個向量之間的線性插值。按照數字t在from到to之間插值。

static functionLerp (from : Vector4, to : Vector4, t : float) : Vector4

兩個向量之間的線形插值。按照數字t在from到to之間插值。t是夾在[0...1]之間的值。,當t = 0時,返回from。當t = 1時,返回to。當t = 0.5 返回from和to的平均數。

5.Mathf.Lerp 插值

static functionLerp (from : float, to : float, t : float) : float

基於浮點數t返回a到b之間的插值,t限制在0~1之間。當t = 0返回from,當t = 1 返回to。當t = 0.5 返回from和to的平均值。

6.Color.Lerp 插值

static functionLerp (a : Color, b : Color, t : float) : Color

通過t在顏色a和b之間插值。

"t"是夾在0到1之間的值。當t是0時返回顏色a。當t是1時返回顏色b。

插值,從字面意思上看,就是在其間插入一個數值,這種理解是否正確呢?我們先從最簡單的浮點數插值函式來分析:

Mathf.Lerp 插值

static functionLerp (from : float, to : float, t : float) : float

基於浮點數t返回a到b之間的插值,t限制在0~1之間。當t = 0返回from,當t = 1 返回to。當t = 0.5 返回from和to的平均值。

首先,我們來做一個試驗,啟動Unity3D,任建一個指令碼檔案,在其Start()中輸入內容如下:

void Start () {

        print(Mathf.Lerp(0.0f, 100.0f,0.0f).ToString());

        print(Mathf.Lerp(0.0f, 100.0f,0.1f).ToString());

        print(Mathf.Lerp(0.0f, 100.0f,0.2f).ToString());

        print(Mathf.Lerp(0.0f, 100.0f,0.3f).ToString());

        print(Mathf.Lerp(0.0f, 100.0f,0.4f).ToString());

        print(Mathf.Lerp(0.0f, 100.0f,0.5f).ToString());

        print(Mathf.Lerp(0.0f, 100.0f,0.6f).ToString());

        print(Mathf.Lerp(0.0f, 100.0f,0.7f).ToString());

        print(Mathf.Lerp(0.0f, 100.0f,0.8f).ToString());

        print(Mathf.Lerp(0.0f, 100.0f,0.9f).ToString());

        print(Mathf.Lerp(0.0f, 100.0f,1.0f).ToString());

    }

執行Unity,在控制檯將打印出:


       這個實驗是在0到100之間插值,插入什麼值,取決於第3個引數,從列印結果可看出,第3個引數是個比例因數,是0.1時表示0到100這個長度的十分之一,同理,0.2表示十分之二,依此類推。從這點上看來,我們起初從字面上所理解的插值就是插入一個數值是可以這樣理解的。

       如果我們把上面那個腳本里的插值函式裡的第一個引數變為100.0f,第二個引數變為110.0f,第三個引數保持不變,大家想想其執行結果該是什麼呢?可不要認為是0、1、2、3、4、5、6、7、8、9、10了喲,實際結果是100、101、102、103、104、105、106….,因插值是把值插在原來的兩數之間,這說明這個函式首先是根據第三個引數所給定的比例算出淨增量,再加上起始數,最終算出插值值的。

unity3d遊戲開發中,應用最多的是Vector3.Lerp 向量插值,下面我們以此插值來猜推其內部實現機理以及一些應用。


如圖,在空間中存在兩點A(0,10,0)與B(10,0,-10),我們在A、B兩點間插入一C點,假設C點的位置在AB的五分之二處,即AC/AB=0.4,根據相似圖形對應邊成比例的初中幾何知識可知,在⊿ABO中AC/AB=OD/OB,同理在⊿OBF中OD/OB=OE/OF,所以AC/AB=OD/O=OE/OF = 0.4,則C點的X座標值為:OE=0.4*OF=0.4*10=4。

根據上圖,還可知ED/FB=0.4,所以C點的Z座標值DE=0.4*BF=0.4*(-10)=-4。

C點的Y座標值請看下圖:


EO/AO=DF/AF=CB/AC=1-0.4=0.6,則C點的Y座標值EO=0.6*AO=0.6*10=6。

綜上所述,C點的三維座標為C(4,6,-4)。

下面我們利用Unity3D中的Vector3.Lerp 插值函式:static function Lerp (from :Vector3, to :Vector3, t : float) :Vector3來計算上面演算的插值。

我們把先前指令碼中的Start()函式改寫成:

void Start()

    { print(Vector3.Lerp(newVector3(0, 10, 0),newVector3(10, 0, -10), 0.4f).ToString()); }

其執行結果為:


這與我們的演算結果是一致的。

上面的演算,我們為了簡便,A、B兩點取得較特殊,降低了演算的複雜度。而對普通的A、B兩點,如下圖所示:


       我們同樣可以得到三角形EGL與三角形EFK,使用同樣的方法可計算出HI的長度,再加上OH的長度就是C點的X座標值了。同樣的方法可推演出Y與Z的座標。手工計算是很複雜的,而Lerp函式可以高效地為我們返回這個插值的,我們在這裡做出的演算,只是幫助我們來推測Lerp這個函式的內部實現機理而也,實際運用中,一切工作都是交Lerp函式去完成。

        Lerp函式在遊戲開發過程使用較多,在Unity的幫助文件裡就有為我們列舉了Vector3.Lerp的兩個應用的例子,一個是在1秒時間動畫位置移動從start.position開始到end.position結束:

[csharp] view plain copy  print?
  1. using UnityEngine;  
  2. usingSystem.Collections;  
  3. public classexample : MonoBehaviour   
  4. {  
  5.          public Transform start;  
  6.          public Transform end;  
  7.          void Update()   
  8.          {  
  9.                    transform.position =Vector3.Lerp(start.position, end.position, Time.time);  
  10.          }  
  11. }  


另一個例子:

//像彈簧一樣跟隨目標物體

[csharp] view plain copy  print?
  1. using UnityEngine;  
  2. usingSystem.Collections;  
  3. public classexample : MonoBehaviour   
  4. {  
  5.          public Transform target;  
  6.          publicfloat smooth = 5.0F;  
  7.          void Update()   
  8.          {  
  9.                    transform.position =Vector3.Lerp(transform.position, target.position, Time.deltaTime * smooth);  
  10.          }  
  11. }  


這個例子中的transform.position是去跟隨的那個物體的空間座標,target.position是目標物體的空間座標,整句的結果是讓跟隨物體的座標不斷地變化為它們兩者之間的插值,然而隨著時間的推移,第三個引數的值最終會為1,所以最終跟隨物體的位置會與目標物體重合的。我們以前所玩的遊戲中,主人公身上依附著一隻寵物如鷹,主人公移動時,鷹會跟隨著飛動,主人公移動得快它就飛行跟動得快,始終不會離開主人公,使用Lerp插值函式就可實現。

下面我們來看另一個應用例項。


       這是酷跑遊戲場景,囚犯沿著一條森林道路向前奔跑,後面有警車追趕,前面有路障,在遊戲過程中,我們要在囚犯奔跑的固定路線上隨機產生路障,而道路不是平直的,既左右彎曲,又上下起伏,由程式隨機生成的路障怎樣確定其空間位置呢?這時,Lerp函式就派上了用場。

        先根據道路的彎曲與起伏,在轉折處設定一個空物體,此空物體的Position值即空間座標與此處道路一致,我們把這些空物體所在的點稱為道路轉折點,這些點連線而成的線段所組成的多段折線貼合在路面上,是這條道路的近似路徑,這些點取得越多、越準確,這條路徑與道路的相似程度就越高。

現在我們用那條路徑來代替那條道路,把隨機產生的路障放在這條路徑上也就是放在道路上了。

        假設我們想每隔100米至200米之間產生一個路障,用變數z += Random.Range(100, 200)記錄下該路障的Z座標值(因囚犯總體上是沿著Z軸往前跑)然後根據此Z座標值判斷該座標值在前面所設定的轉折點中的哪兩個點之間,找到後就在這兩個點之間插值,其插值的比例因數(Lerp()函式的第3個引數)可由兩個轉折點與這個插值點這三個點中已知的Z座標值算出來,這樣Vector3.Lerp (from : Vector3, to :Vector3, t : float)函式中的三個引數值便都是已知的了,它就可計算出這個插值點的空間座標了,根據前面的設計,這兩個轉折點之間的線段是貼合在路面上的,那麼此插值的座標也就是在路面上了,根據此插值放置的路障也就不會偏離道路,且會隨著道路的左轉而左轉,右轉而右轉,上坡而上坡,下坡而下坡了。

具體設計過程如下:

        匯入道路模型,假設命名為forest_1。模型設計時就確定好了其長度為3000、座標原點在其終端上了的。匯入後我們將其沿Z軸正方向放置在場景中,讓其Transorm.Position的X、Y值均為0。我們可以匯入多段同類型的道路模型,通過控制它們的Z值來把它們拼接成長長的森林道路。

        在此道路物體上新建一個空物體作為它的子物體,命名為waypoint,再在其下建立多個為空的空物體,分別命名為waypoint_01、waypoint_02……,把它們放在道路的轉折處,並通過放大、旋轉場景圖後細調這些孫物體的座標值,使它們與道路路面貼合,如下圖所示:


說明:圖中的綠色按鈕狀塊就是這些空物體,因它們是空物體,不能顯示在場景中,是通過屬性面板給它們設定了一個供編輯時顯示使用的圖示標示。

 

這樣,我們便把彎彎曲曲的道路分成了一段一段的直路段,並記錄下來了各段路段兩端的特徵點的座標值。有了這些特徵點,也就有了與道路相近的路線了。這是化曲為直的方法,把彎曲、起伏的道路化成了與此相近的一段一段的線段。這樣的點越多,其相似程度越高。

在waypionts上建立一個指令碼元件waypionts.cs:

[csharp] view plain copy  print?
  1. using UnityEngine;  
  2. using System.Collections;  
  3. publicclass waypoints : MonoBehaviour  
  4.  {  
  5.     public Transform[] points;  
  6.     void OnDrawGizmos()  
  7.     {  
  8.         iTween.DrawPath (points);  
  9.     }  
  10. }  


public Transform[]points;該句所定義的points就是存放那些特徵點的陣列,因它是public,可在Unity編輯介面中為其賦值,其操作方法是先在Hierarchy檢視中選中waypoints控制元件,然後在其Inspector檢視中點選圖示鎖住其Inspector面板,然後在Hierarchy檢視中全選waypoint_01至waypiont_11後拖到屬性面板上的陣列名points上即可完成賦值,如下圖:

 

接下來,在這個森林道路上建立的Forestcs.cs指令碼元件裡新增生成路障的指令碼:

[cpp] view plain copy  print?
  1. <span style="background-color: rgb(255, 255, 153);">using UnityEngine;  
  2. using System.Collections;  
  3. publicclass Forest : MonoBehaviour   
  4. {  
  5.     public GameObject[] obstacles;     //路障物體陣列
  6.     publicfloat startLength = 50;   //路障在道路上出現的開始位置
  7.     publicfloat minLength = 100;   //路障距上一個路障的最小距離
  8.     publicfloat maxLength = 200;   //路障距上一個路障的最大距離
  9.     private Transform player;        //遊戲主人公-奔跑者的Transform元件
  10.     private waypoints wayPoints;    //與路面相貼合的路線上的指令碼元件
  11.     void Awake()   
  12.     {  
  13.         player = GameObject.FindGameObjectWithTag(Tags.player).transform; //找到遊戲主人公-奔跑者並獲得它的Transform元件
  14.         wayPoints = transform.Find("waypoints").GetComponent<waypoints>();  //找到與路面相貼合的路線上的指令碼元件
  15.     }  
  16.     // Use this for initialization
  17.     void Start()  
  18.     {  
  19.         GenerateObstacle();    //當森林道路被創建出來時,就會自動呼叫此Start()方法,從而呼叫此GenerateObstacle()方法
  20.     }  
  21.     // 如果主人公跑完了這段道路,則通知GenerateForest類開始執行產生新的道路,並銷燬已跑完的這條道路
  22.     void Update ()   
  23.     {  
  24.         if (player.position.z > transform.position.z+100)   
  25.         {  
  26.             Camera.main.SendMessage("GenerateForest");  
  27.             GameObject.Destroy(this.gameObject);  
  28.         }  
  29.     }  
  30.     void GenerateObstacle()  
  31.     {  
  32.         float startZ = transform.position.z - 3000;  //當前道路在場景中的起始Z座標
  33.         float endZ = transform.position.z;          //當前道路在場景中的結束Z座標
  34.         float z = startZ + startLength;             //將要產生的路障的Z座標
  35.         while (true)   
  36.         {  
  37.             z += Random.Range(100, 200);            //每隔100多米的距離產生一個路障
  38.             if (z > endZ)                           //如果將要產生路障的位置超出了這條道路則退出路障產生迴圈,否則產生路障
  39.             {  
  40.                 break;  
  41.             }  
  42.             else
  43.             {  
  44.                 Vector3 position = GetWayPosByz(z);                    //呼叫GetWayPosByz()方法計算路障位置座標
  45.                 int obsIndex = Random.Range(0, obstacles.Length);      //產生一個從路障數組裡取路障的隨機序數
  46.                 GameObject.Instantiate(obstacles[obsIndex], position, Quaternion.identity);//例項化路障
  47.             }  
  48.         }  
  49.     }  
  50.     Vector3 GetWayPosByz(float z)   
  51.     {  
  52.         Transform[] points = wayPoints.points;       //在道路上設定的轉折點的集合
  53.         int index = 0;                               //轉折點在集合中的序數號
  54.         for (int i = 0; i < points.Length-1; i++)   
  55. 相關推薦

    Unity3D線性Lerp()函式解析

    在unity3d中經常用線性插值函式Lerp()來在兩者之間插值,兩者之間可以是兩個材質之間、兩個向量之間、兩個浮點數之間、兩個顏色之間,其函式原型如下: function Lerp(start : Material, end : Material, t : f

    【短道速滑一】OpenCVcvResize函式使用雙線性縮小影象到長寬大小一半時速度飛快(比最近鄰還快)之異象解析和自我實現。

      今天,一個朋友想使用我的SSE優化Demo裡的雙線性插值演算法,他已經在專案裡使用了OpenCV,因此,我就建議他直接使用OpenCV,朋友的程式非常注意效率和實時性(因為是處理視訊),因此希望我能測試下我的速度和OpenCV相比到底那一個更有速度優勢,恰好前一段時間也有朋友有這方面的需求,因此我就隨意編

    最近鄰和雙線性的基本原理 以及OpenCVresize函式的用法改變影象的大小

    最近鄰插值和雙線性插值的基本原理 影象的縮放很好理解,就是影象的放大和縮小。傳統的繪畫工具中,有一種叫做“放大尺”的繪畫工具,畫家常用它來放大圖畫。當然,在計算機上,我們不再需要用放大尺去放大或縮小影象了,把這個工作交給程式來完成就可以了。下面就來講講計算機怎麼來放大縮小圖象;在本文中,

    MATLAB線性

    %原始資料 n=[0,1,2,3,4,5,6,7,8,9]; y=[1.5,2,2.5,3,3.5,4,4.5,5,5.5,6]; subplot(1,2,1) stem(n,y); title('原始

    Lerp&Slerp(線性與球型線性)

    解決問題(1.模型轉身沒有緩動 2.從走路到跑步沒有緩動) ⭐1.if (pi.Dmag > 0.1f)//增加判斷 如果按鍵時間大於0.1秒那麼就不轉回到前面 { Vector3 targetForward = Vector3.Slerp(model.transform.forwar

    影象放縮最近鄰和雙線性的基本原理

    影象的縮放很好理解,就是影象的放大和縮小。傳統的繪畫工具中,有一種叫做“放大尺”的繪畫工具,畫家常用它來放大圖畫。當然,在計算機上,我們不再需要用放大尺去放大或縮小影象了,把這個工作交給程式來完成就可以了。下面就來講講計算機怎麼來放大縮小圖象;在本文中,我們所說的影象都是指

    C# 分段線性函式

    由於專案需要,需要將資料採集得到的點數轉化為固定點數,使用分段線性插值其實現程式碼如下: ///<summary> ///分段線性插值,將一組數插值為所需點數 ///</summary> ///<param name="dataIn">待插

    圖像算法

    nbsp ear logs 最近鄰 splay cnblogs width itl line 1. 最近鄰插值 2. 雙線性插值 圖像中的插值算法

    Opencv(C++)實現二階線性

    i++ -- alt key ++ enc 新的 round idt #include<opencv2\opencv.hpp> #include<iostream> using namespace cv; using namespace std;

    numpy的一維線性函數

    crete linspace 谷歌 nal 樣本 其中 arr clas info 前言: ? ? ?在用生成對抗網絡生成二維數據點的時候遇到代碼裏的一個問題,就是numpy中的一維線性插值函數interp到底是怎麽用的,在這個上面費了點功夫,因此現將其用法給出。 ? ?

    幾何角度理解線性和雙線性

    表示 兩種 容易 灰度 圖片 技術分享 方塊 描述 浮點 已知兩個點的坐標為\((x0,y0)\)和\((x1,y1)\),他們呈簡單的線性關系(或者近似)。帶求坐標x落在\((x0,x1)\)之間,求y。 如上圖白色的線。 從幾何角度有兩種方法,一是相似三角形,二是斜率

    [UE4]非常實用的Lerp

    erp com 之間 mage 技術分享 eva == rev bsp Alpha的數值範圍是0到1。 if(Alpha==0) ReturnValue=A if(Alpha==1) ReturnValue=B 如果Alpha在0到1之間,Alpha值越接近

    圖像縮放——雙線性算法

    val 位置 單位 sso 數學 圖像 取值 利用 等待   在數學上,雙線性插值是有兩個變量的插值函數的線性插值擴展,其核心思想是在兩個方向分別進行一次線性插值。如果選擇一個坐標系統使得 的四個已知點坐標分別為 (0, 0)、(0, 1)、(1, 0) 和 (1, 1)

    影象演算法的基礎知識(雙線性,協方差矩陣,矩陣的特徵值、特徵向量)

    0. 前言 MATLAB或者OpenCV裡有很多封裝好的函式,我們可以使用一行程式碼直接呼叫並得到處理結果。然而當問到具體是怎麼實現的時候,卻總是一臉懵逼,答不上來。前兩天參加一個演算法工程師的筆試題,其中就考到了這幾點,感到非常汗顏!趕緊補習! 1. 雙線性插值 在影象處

    線性的影象縮放問題

           初次開始寫部落格,想記錄下自己在公司實習所做過的事情以及學習到的東西,雖然還是有很多東西不瞭解也還沒做出來,但是也希望這是一種體驗。        我於2018.9.3入職進行實習,到現在也快過去兩個月了,我在公司

    Vue閃爍問題

         使用{{}}方式在網速較慢時會出現問題。在資料未載入完成時,頁面會顯示出原始的{{}},載入完畢後才顯示正確資料,我們稱為插值閃爍。 尤其在網速比較慢的時候比較明顯。 【解決方案】使用v-text和v-html指令來替代{{}} 。

    線性,雙線性Bilinear Interpolation演算法

    線性插值 先講一下線性插值:已知資料 (x0, y0) 與 (x1, y1),要計算 [x0, x1] 區間內某一位置 x 在直線上的y值(反過來也是一樣,略): y−y0x−x0=y1−y0x1−x0y−y0

    OpenCV---如何對影象進行雙線性運算(7)

    附程式碼如下: import cv2 as cv import numpy as np def resize(): src = cv.imread("D:/matplotlib/0.jpg") cv.imshow("input",src) h, w = src.shape

    線性/華為機試(C/C++)

    題目描述 訊號測量的結果包括測量編號和測量值。存在訊號測量結果丟棄及測量結果重複的情況。    1.測量編號不連續的情況,認為是測量結果丟棄。對應測量結果丟棄的情況,需要進行插值操作以更準確的評估訊號。   採用簡化的一階插值方法,由丟失的測量結果兩頭的測量值算出兩者中

    影象演算法-最近鄰線性

    影象插值演算法包括向上插值和向下插值,向上插值就是對影象進行放大,向下插值就是對影象進行縮小,插值演算法在影象預處理過程中經常被使用,通過插值演算法,可以將影象尺寸變換為任意尺寸,下面以舉例子的方式來說明兩種常見的插值演算法: 假設影象原始尺寸為wi,hi,縮