亚洲视频在线一区二区三区_色婷婷AV一区二区三区浪潮_亚洲综合无码精品一区二区三区_高清精品一区二区三区一区

如何用Unity制作逼真的自然場景

作者:直尚教育
2020-03-26
631

游戲中的風景越真實、自然,玩家的代入感也就越強。那如何用Unity制作逼真的自然場景? 學習群:233763660

游戲中的風景越真實、自然,玩家的代入感也就越強。那如何用Unity制作逼真的自然場景?本文將分析Unity官方制作的演示短片——《Book of the Dead》中,風與植物的交互原理,幫助大家制作出更自然的風景。



《Book of the Dead》是由Unity官方制作的一個演示短片,其中有大量的植物,不僅渲染上有著照片級的真實感,而且風與植物的交互也非常的自然。其所有的自然資源都是來自照片掃描技術,而且使用了HDRP高清渲染管線,場景在Unity Asset Store上可以下載得到。本文主要分析短片中風與植物交互的原理。



一、支持的植物結構


場景中,對于風與植物交互的模擬,支持三種結構:


Hierachy Pivot:層次嵌套Pivot,用于模擬樹或者其他有多重層次結構的植物


Single Pivot Color:單Pivot,用于模擬草


Procedural Animation:程序動畫,用于模擬浮萍等無pivot的植物



對于樹的模擬最為復雜,它屬于Hierachy Pivot結構,最多支持3個層次嵌套:


主干,連接地面


Level 0分支,連接著主干


Level 1分支,連接著Leval 0分支



二、代碼入口


本文重點分析Hierachy Pivot結構的實現(xiàn)原理。風與植物的交互一般用程序頂點動畫實現(xiàn),隨意找到一棵樹的shader,順藤摸瓜可以在VS中找到如下代碼:


可以看到,每個頂點的uv3通道中存的是pivot信息,即該頂點受哪些pivot影響。


#if USE_VEGETATION_ANIM


    float3 positionWS = GetAbsolutePositionWS(positionRWS);


    APPLY_VEGETATION_ANIM_TIMENUDGE(positionWS, normalWS, input.uv3/*pivotData*/, input.color.rgb/*pivotColor*/, GetObjectAbsolutePositionWS(), time.x);


    positionRWS = GetCameraRelativePositionWS(positionWS);


#endif


注意的是,這里的uv3是float3類型。


struct AttributesMesh


{


//...


//forest-begin: Added vertex animation


#if defined(_ANIM_SINGLE_PIVOT_COLOR) || defined(_ANIM_HIERARCHY_PIVOT)


    float3 uv3          : TEXCOORD3;


//...


};


Hierachy Pivot結構的植物,最終調用的是AnimateVegetationHierarchyPivot。


#if defined(USE_VEGETATION_ANIM) && defined(_ANIM_SINGLE_PIVOT_COLOR)


    #define APPLY_VEGETATION_ANIM_TIMENUDGE(worldPos, normalWorld, pivotData, pivotColor, objectRoot, timeNudge) { AnimateVegetationSinglePivot(worldPos, normalWorld, pivotData, pivotColor, timeNudge); }


#elif defined(USE_VEGETATION_ANIM) && defined(_ANIM_HIERARCHY_PIVOT)


    #define APPLY_VEGETATION_ANIM_TIMENUDGE(worldPos, normalWorld, pivotData, pivotColor, objectRoot, timeNudge) { AnimateVegetationHierarchyPivot(worldPos, normalWorld, pivotData, pivotColor, objectRoot, timeNudge); }


#elif defined(USE_VEGETATION_ANIM) && defined(_ANIM_PROCEDURAL_BRANCH)


    #define APPLY_VEGETATION_ANIM_TIMENUDGE(worldPos, normalWorld, pivotData, pivotColor, objectRoot, timeNudge)  { AnimateVegetationProceduralBranch(worldPos, normalWorld, objectRoot, timeNudge); }


三、PivotData解碼


pivotData是float3類型,先用asuint轉成uint3,一共是32x3=96個bit,分成兩段前后48bit,分別存Pivot0和Pivot1的信息,分別用UnpackPivot0和UnpackPivot1解出來。


    uint3 packedData =asuint(pivotData);


    float3 pivotPos0, pivotPos1, pivotFwd0, pivotFwd1;


    bool pivotEnabled0 =UnpackPivot0(packedData, pivotPos0, pivotFwd0);


    bool pivotEnabled1 =UnpackPivot1(packedData, pivotPos1, pivotFwd1);


Pivot0和Pivot1剛好是對稱排列。



接下來分析Pivot0是如何解碼出來的,48bit里面,高32bit存Pivot Pos,低16位存Pivot Fwd,細節(jié)如下圖所示。最終解出來的Pos是模型空間的坐標,樹的建模應該是樹干的根在模型空間的原點,Pos.x和Pos.z是有正負的,而樹只能向上長,于是Pos.y必然是大于0。對于一般的樹而言垂直方向范圍一般大于水平方向的范圍,于是用12bit保存Pos.y的值,稍微比x和z多2個bit的精度。



滿足packedData.y & 0xFFFF0000時,即高16位有值時,代表有Pivot0的信息,才需要解析。


// Needs to match shader packing in baking tool


bool UnpackPivot0(uint3 packedData, inout float3 pivotPos0, inout float3 pivotFwd0) {


    if(packedData.y & 0xFFFF0000) {


        pivotPos0.x = UnpackFixedToSFloat(packedData.x, 8.f, 10, 22);


        pivotPos0.y = UnpackFixedToUFloat(packedData.x, 32.f, 12, 10);


        pivotPos0.z = UnpackFixedToSFloat(packedData.x, 8.f, 10, 0);


        pivotFwd0.x = UnpackFixedToSFloat(packedData.y, 1.f, 8, 24);


        pivotFwd0.z = UnpackFixedToSFloat(packedData.y, 1.f, 7, 17);


        pivotFwd0.y = sqrt(1.f - saturate(dot(pivotFwd0.xz, pivotFwd0.xz))) * (((packedData.y >> 16) & 1) ? 1.f : -1.f);


        pivotFwd0 = normalize(pivotFwd0);


        return true;


    }


    return false;


}


其中Pos.x用UnpackFixedToSFloat解出來,10bit實際存的是百分比[0, 1],由于x可能是負數(shù),編碼時把[-1, 1]映射到[0, 1],于是這里把[0, 1]反映射回[-1, 1],再乘以傳入的range,可以看出Pos.x的范圍是[-8f, 8f]。從其他硬編碼的參數(shù)可以看出,樹的建模尺寸是長寬16x16,高是32。


float UnpackFixedToSFloat(uint val, float range, uint bits, uint shift) {


    const uint BitMask = (1 << bits) - 1;


    val = (val >> shift) & BitMask;


    float fval = val / (float)BitMask;


    return (fval * 2.f - 1.f) * range;


}


Fwd是分支(樹干或者樹枝)的方向,由于是單位向量,所以只存了x和z分量,y分量可以通過公式反算出來,開方后丟失了符號信息,于是用1位存符號。


pivotFwd0.x = UnpackFixedToSFloat(packedData.y, 1.f, 8, 24);


        pivotFwd0.z = UnpackFixedToSFloat(packedData.y, 1.f, 7, 17);


        pivotFwd0.y = sqrt(1.f - saturate(dot(pivotFwd0.xz, pivotFwd0.xz))) * (((packedData.y >> 16) & 1) ? 1.f : -1.f);


        pivotFwd0 = normalize(pivotFwd0);


四、整體流程


偽代碼如下所示,有點跟骨骼動畫類似,頂點受骨骼的變換影響,而每個骨骼會受其父骨骼的變換影響,最終頂點受骨骼的級聯(lián)變換影響。樹的頂點至少受主干的影響,因為任何頂點肯定要么是屬于主干或者屬于其他分支,而其他分支必然直接或間接連著主干,最復雜的情況是頂點在level1分支上,level1分支連著level0分支,level分支連著主干,需要計算累計變換。


//任何頂點肯定是在主干上或連接著主干


    計算主干受風力影響導致的旋轉;


    旋轉作用于頂點pos和normal;


    if (有pivot0信息)//主干連接著level0分支


    {


        計算level0分支受風力影響導致的旋轉;


        旋轉作用于頂點pos和normal;


        if (有pivot1信息)//level0分支連接著level1分支


        {


            計算level1分支受風力影響導致的旋轉;


            旋轉作用于頂點pos和normal;


        }


    }


4.1 主干受風影響


對于每個枝干受風吹后彎曲程度,由如下變量控制,整體的彎曲程度可以由Wind Elasticity Lvl x系列變量控制,其中Lvl B是主干。



樹被風吹有個特點,離地面越遠部分,被吹彎曲的越厲害,所以會有個縮放系數(shù)來控制旋轉量,地面處為0(樹根),離地面越遠的部分這個縮放系數(shù)越大。有種預烘培做法,是用頂點模型空間的y除以整個樹的高度,計算出縮放系數(shù)并把它烘到點色或者其他通道上,這里的做法是運行時計算,用變量_WindRangeLvlB調節(jié)受風的范圍,它是模型空間的量,其實跟預烘培的效果差不多。lvBElasticity是最終的彈性縮放系數(shù)。


//主干風力影響代碼


    float lvBRelativeObjectScale = mul(GetActualObject2World(), float4(0, _WindRangeLvlB, 0, 0)).y;


    float3 windFwd = GetWindDirection(objectRoot);


    float3 lvBBaseGustWind = GetTreeBaseGustWind(objectRoot, timeNudge);


    float3 lvBPos = objectRoot;


    //主干fwd直接取模型空間y軸方向


    float3 lvBFwd = float3(0, 1, 0); //TODO: grab from rotation matrix


    float lvBElasticity = _WindElasticityLvlB;


    float lvBDistScale = saturate((worldPos.y - objectRoot.y) / lvBRelativeObjectScale);


    lvBElasticity *= lvBDistScale;


對于風吹草的模擬一般在頂點加上風力方向的偏移就可以得到比較好的效果,因為一般草都比較矮小,但是對于樹這種比較高的復雜結構,用草的方式模擬會有種樹被拉扯變長的感覺,所以一般的方案是用旋轉代替頂點偏移。


lvBWindAxis為旋轉軸,windFwd與lvBFwd如果同向或者反向時候,主干應該是不會旋轉,后面的枝干level 0做了這種情況的修正,主干這里可能從設計上就不會有垂直于地面的風向吧。


然后就是把旋轉作用到頂點的pos和normal上,旋轉的錨點是世界空間下的objectRoot,應該是模型空間的原點。


    float lvBWindRotAngle = lvBBaseGustWind.x * lvBElasticity;


    //對旋轉角度進行l(wèi)og2衰減


    lvBWindRotAngle = log2(1.f + abs(lvBWindRotAngle)) * sign(lvBWindRotAngle);


    float3 lvBWindAxis = cross(lvBFwd, windFwd);


    float4 lvBWindQuat = QuaternionFromAxisAngle(lvBWindAxis, lvBWindRotAngle);


    worldPos = QuaternionRotatePointAbout(worldPos, lvBPos, lvBWindQuat);


    worldNrm = QuaternionRotateVector(worldNrm, lvBWindQuat);


4.2 支干Level0受風影響


邏輯基本與主干差不多,不同的地方是lv0Fwd的方向用lv0BaseGustWind和lvBDistScale做了調整,猜測是為了讓彎曲旋轉更自然。


旋轉角度lv0WindRotAngle根據(jù)windFwd和lv0Fwd的是否平行,進行了相應的衰減。


    //當lv0BaseGustWind.y為0時,平行風,旋轉軸為y軸模擬更自然


    lv0Fwd.y *= lv0BaseGustWind.y * lvBDistScale;


    lv0Fwd = normalize(lv0Fwd);


    float3 lv0WindAxis = cross(windFwd, lv0Fwd);


    float3 lv0WindRight = cross(windFwd, lv0WindAxis);


    float lv0PerpendicularFactor = dot(lv0Fwd, lv0WindRight);


    float lv0AngleFactor = lv0PerpendicularFactor * lv0PerpendicularFactor;


    lv0AngleFactor *= sign(lv0PerpendicularFactor);


    lv0WindRotAngle *= lv0AngleFactor;


4.3 支干Level1受風影響


Level1是樹最外層的部分了,整體流程與LevelB和Level0差不多,另外多了一些撲動的處理,樹的外端末枝(樹葉或者小樹枝)被風吹的時候往往是有較劇烈的搖晃,而且呈一定的隨機周期性運動,這部分計算出來的是頂點偏移,并在最后的旋轉前先加到頂點坐標上。


    float vertexFlutterPhase = dot(worldPos, _WindFlutterPhase);


    float windFlutterCos = cos(WIND_PI2 * (_WindTime + timeNudge + vertexFlutterPhase) / (_WindTreeFlutterGustVariancePeriod * _WindFlutterPeriodScale));


    float windFlutterStrength = lv1Elasticity * _WindFlutterElasticity * _WindFlutterScale * (_WindTreeFlutterStrength + saturate((max(0.f, lv0BaseGustWind.z) - _WindTreeFlutterGustStrengthOffset) / _WindTreeFlutterGustStrengthScale) * _WindTreeFlutterGustStrength);


    float3 lv1WindAxis = cross(windFwd, lv1Fwd);


    worldPos += lv1WindAxis * windFlutterCos * windFlutterStrength;


文 | Kirk


騰訊互動娛樂 工程師




直線網(wǎng)公眾號,第一時間學習最新教程,看最新行業(yè)動態(tài)!!


0
0
分享到:

0

喜歡他,就推薦他上首頁吧^_^

推薦閱讀

×

賽事服務聯(lián)系方式

0371-86068866

4008887269

cndesign@163.com

好的,我知道了

官方微信

聯(lián)系我們

  • QQ:33143335 QQ:1904200230
  • 電話:18569912460
  • 投稿:cndesign@163.com
  • 地址:鄭州市國家大學科技園東區(qū)9號樓2層

版權信息

  移動 Android 版 豫 ICP 備16038122號-2 豫公網(wǎng)安備 41019702002261號

亚洲视频在线一区二区三区_色婷婷AV一区二区三区浪潮_亚洲综合无码精品一区二区三区_高清精品一区二区三区一区

      9000px;">

          最新不卡av在线| 波波电影院一区二区三区| 韩国三级在线一区| 成人精品在线视频观看| 欧美日韩日日骚| 亚洲黄色小视频| 国产盗摄一区二区| 丁香婷婷综合网| 亚洲色欲色欲www在线观看| 免费成人结看片| 精品国产免费久久| 盗摄精品av一区二区三区| 欧美一区二区不卡视频| 久久久久久久综合色一本| 国内久久精品视频| 轻轻草成人在线| 亚洲色图欧洲色图| 精品一区二区三区不卡| 亚洲欧美福利一区二区| 久久亚洲影视婷婷| 91在线精品一区二区三区| 日韩福利电影在线观看| 欧美激情一二三区| 欧美三级欧美一级| 99久久精品国产麻豆演员表| 国产精品一区二区91| 久久久99久久| 欧美日韩高清一区| 99亚偷拍自图区亚洲| 午夜精品一区二区三区电影天堂 | 日韩一级二级三级| 欧美韩国日本不卡| 欧美午夜一区二区三区| 国产91色综合久久免费分享| 成人性生交大片免费看中文 | 成人高清免费在线播放| 精品视频免费在线| 在线视频观看一区| 成人av资源站| 国产成人自拍网| 欧美日韩中文精品| 亚洲影视在线播放| 一二三四社区欧美黄| 欧美日韩一区二区三区在线看| 99综合影院在线| 精品国产一区二区三区不卡| 亚洲第一综合色| 91麻豆成人久久精品二区三区| 樱花草国产18久久久久| 欧美日本视频在线| 国产精品色噜噜| 日本美女一区二区| 日韩午夜激情av| 国产亚洲精品久| 国产日本一区二区| 亚洲丝袜自拍清纯另类| www亚洲一区| 岛国一区二区在线观看| 国产亚洲女人久久久久毛片| 2020国产成人综合网| 日韩电影在线免费观看| 亚洲第一综合色| 国产精品热久久久久夜色精品三区 | 欧美激情一区二区三区不卡| 成人av免费网站| av电影在线不卡| 91精品国产综合久久久久久漫画| av在线不卡电影| 欧美视频一区在线| 6080国产精品一区二区| 色偷偷88欧美精品久久久| 国产日韩精品一区二区三区在线| 久久久亚洲精品一区二区三区| 日韩中文字幕不卡| 久久久久久9999| 国产精品欧美综合在线| 一区二区三区波多野结衣在线观看 | 国产美女一区二区| 国产成人av资源| 秋霞国产午夜精品免费视频| 欧美久久一二区| 欧美一区二区三区视频在线 | 国产一区二区不卡在线| 在线观看国产一区二区| 久久久精品综合| 蜜臀av性久久久久蜜臀av麻豆| 日韩视频免费观看高清在线视频| 国产乱子轮精品视频| 欧美一区欧美二区| 欧美精品久久99久久在免费线| 夜夜操天天操亚洲| 性做久久久久久免费观看| 日韩视频在线你懂得| 免费在线观看视频一区| 久久蜜桃香蕉精品一区二区三区| 精彩视频一区二区| 欧美aaaaaa午夜精品| 国产综合成人久久大片91| caoporn国产精品| 日韩精品一区二区三区蜜臀| youjizz久久| 青青草国产成人av片免费| 亚洲免费视频中文字幕| 视频一区二区国产| 国产99久久久国产精品潘金 | 国内精品自线一区二区三区视频| 国内一区二区视频| 色婷婷综合久久久中文一区二区 | 美女视频免费一区| 午夜视频久久久久久| 亚洲国产精品久久一线不卡| 在线视频国内自拍亚洲视频| 专区另类欧美日韩| 成人av免费在线| 亚洲国产精品成人久久综合一区| 国产精品中文欧美| 欧美一区二区女人| 欧美一区二区三区免费| 久久91精品国产91久久小草 | 国产·精品毛片| 精品国产91九色蝌蚪| 亚洲国产日韩综合久久精品| 在线欧美日韩精品| 亚洲精品视频免费看| 日本伊人色综合网| 日韩你懂的在线观看| 国产成人综合亚洲91猫咪| 久久综合九色综合97婷婷女人| 久久99久久精品| 久久久一区二区三区捆绑**| 99r精品视频| 欧美性xxxxxx少妇| 国产精品国产三级国产有无不卡 | 欧美日韩中字一区| 在线免费观看一区| 日韩你懂的在线观看| 91免费在线视频观看| 精一区二区三区| 国产欧美精品一区| 国产一区在线视频| 综合激情成人伊人| 成人性生交大片免费| 亚洲一区二区在线免费看| 国产成人一区二区精品非洲| 成人av电影在线观看| 午夜伦欧美伦电影理论片| 一区二区三区在线看| 狠狠色丁香九九婷婷综合五月| 中文字幕亚洲区| 在线观看一区二区精品视频| 极品少妇一区二区| 久久九九全国免费| 色婷婷综合激情| 老司机午夜精品| 国产精品丝袜黑色高跟| 91精品国产综合久久精品| 人人狠狠综合久久亚洲| 久久不见久久见免费视频1| 亚洲欧洲日产国码二区| 欧美日韩一区小说| 成人黄色免费短视频| 日韩高清不卡一区二区三区| 久久亚洲影视婷婷| 欧美午夜理伦三级在线观看| 国产盗摄精品一区二区三区在线| 午夜精品福利一区二区蜜股av | 日韩中文字幕一区二区三区| 日韩欧美国产综合一区| 日韩午夜精品视频| 国产精品小仙女| 久久黄色级2电影| 国产精品女同互慰在线看| 久久久精品天堂| 欧美日本一区二区| 成人激情小说网站| 怡红院av一区二区三区| 色婷婷精品大在线视频| 天堂蜜桃91精品| 成人午夜视频免费看| 精品日韩一区二区| 狠狠色丁香婷婷综合久久片| 成人激情免费网站| 日韩欧美国产电影| 不卡免费追剧大全电视剧网站| 欧美日韩一区二区三区四区| 久久er精品视频| 国产女同互慰高潮91漫画| 亚洲国产成人porn| 日本韩国一区二区三区| 盗摄精品av一区二区三区| 99久久婷婷国产综合精品| 国产精品久久久久一区二区三区共| 成人av集中营| 91成人看片片| 国产婷婷色一区二区三区| 国产成人综合自拍| 国产一区二区精品久久99| 亚洲一区二区av在线| 亚洲精品国产a| 日韩一区国产二区欧美三区|