[{"data":1,"prerenderedAt":1855},["ShallowReactive",2],{"blog-page-4":3,"blog-count":1854},[4,638,824,1086,1209],{"id":5,"title":6,"body":7,"date":618,"description":13,"extension":619,"meta":620,"navigation":623,"path":631,"seo":632,"stem":633,"tags":634,"__hash__":637},"blogs\u002F_legacy\u002F2021\u002F2021-11-16-note-blue-protocol-anime-render.md","CEDEC2021-《Blueprotocol》的渲染实现笔记",{"type":8,"value":9,"toc":592},"minimark",[10,14,25,28,32,35,38,41,48,51,56,59,62,193,196,199,202,207,210,220,223,228,231,243,248,251,256,261,264,267,272,275,278,281,339,342,346,349,352,360,363,368,371,374,377,385,390,393,396,399,404,407,410,413,418,420,424,427,430,433,438,441,446,449,452,455,469,472,475,480,483,486,489,492,495,498,501,504,507,510,513,516,520,523,526,531,535,538,541,544,549,552,557,561,564,569,579,582,585,588],[11,12,13],"p",{},"CEDEC2021上《BLUE PROTOCOL》的分享的渲染部分。",[11,15,16,17,24],{},"PPT：[",[18,19,23],"a",{"href":20,"rel":21},"https:\u002F\u002Fcedil.cesa.or.jp\u002Fcedil_sessions\u002Fview\u002F2389",[22],"nofollow","BLUE PROTOCOLにおけるアニメ表現技法について　～実装編～"," ]。",[11,26,27],{},"《BLUE PROTOCOL》虽然是用UE4进行实现的，但是由于他们的方法有对引擎进行改造，所以对于不希望进行引擎修改的情况可能不是很有参考作用。",[29,30,31],"h2",{"id":31},"简介",[11,33,34],{},"渲染实现上，是基于UE4的PBR渲染的。为了实现动画风格的渲染，在UE4的材质系统上，添加了一个新的ShaderModel，对BasePass和PostProcess进行了改造。",[11,36,37],{},"这样做的主要目的，一个是可以结合PBR的渲染效果，另一个是降低渲染的成本。",[11,39,40],{},"原始的PTT中有一定的篇幅在介绍UE4原本的渲染过程，由于没有现场的视频，不是很好推断想要表达的内容。总体而言，应当是对UE4原有的渲染过程进行一个总结，为之后介绍修改的部分进行铺垫。",[11,42,43],{},[44,45],"img",{"alt":46,"src":47},"","\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fue4-default-pass-300x156.png",[11,49,50],{},"实际的修改主要集中在对现有的Pass进行改造以及添加一些新的Pass，改造后的Pass流程：",[11,52,53],{},[44,54],{"alt":46,"src":55},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fimage-300x131.png",[11,57,58],{},"详细的修改部分在后面会有，总体而言感觉改动不大。主要修改如介绍说的，主要集中在BasePass上，所以其实单看BasePass的话，修改还是挺大的。",[11,60,61],{},"GBuffer的使用情况：",[63,64,65,87],"table",{},[66,67,68],"thead",{},[69,70,71,75,78,81,84],"tr",{},[72,73,74],"th",{},"#",[72,76,77],{},"Red",[72,79,80],{},"Green",[72,82,83],{},"Blue",[72,85,86],{},"Alpha",[88,89,90,108,125,142,159,176],"tbody",{},[69,91,92,96,99,102,105],{},[93,94,95],"td",{},"GBufferA",[93,97,98],{},"ComputeNomal.x",[93,100,101],{},"ComputeNomal.y",[93,103,104],{},"ComputeNomal.z",[93,106,107],{},"PerObjectGBufferData",[69,109,110,113,116,119,122],{},[93,111,112],{},"GBufferB",[93,114,115],{},"ToonHairMaskOffsetShadowMask",[93,117,118],{},"SpecularMask",[93,120,121],{},"SpecularValue",[93,123,124],{},"ShadingModelIDSelectiveOutputMask",[69,126,127,130,133,136,139],{},[93,128,129],{},"GBufferC",[93,131,132],{},"BaseColor.r",[93,134,135],{},"BaseColor.g",[93,137,138],{},"BaseColor.b",[93,140,141],{},"AmbientOcclusion",[69,143,144,147,150,153,156],{},[93,145,146],{},"GBufferD",[93,148,149],{},"ShadowColor.r",[93,151,152],{},"ShadowColor.g",[93,154,155],{},"ShadowColor.b",[93,157,158],{},"NdotL",[69,160,161,164,167,170,173],{},[93,162,163],{},"GBufferE",[93,165,166],{},"-",[93,168,169],{},"RimLightMask",[93,171,172],{},"DiffuseOffset",[93,174,175],{},"RimLightWidth",[69,177,178,181,184,187,190],{},[93,179,180],{},"GBufferF",[93,182,183],{},"OutlineWidth",[93,185,186],{},"OutlineID",[93,188,189],{},"OutlinePaint",[93,191,192],{},"OutlineZShift",[29,194,195],{"id":195},"光照",[11,197,198],{},"光照使用的Mesh资源总共有两组法线以及顶点颜色数据，两组法线分别是在模型制作工具中预先处理的用于动画风格阴影的法线，以及在导入时自动计算的法线。",[11,200,201],{},"动画风格阴影的法线(Import Normal)没有介绍具体采用的方案，从效果图上看应该采用的是比较通用的处理方式。",[11,203,204],{},[44,205],{"alt":46,"src":206},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fimport-normal-300x240.png",[11,208,209],{},"导入计算的法线(Compute Normal)，有描述计算方式：",[211,212,213,217],"ol",{},[214,215,216],"li",{},"计算三角面的法线",[214,218,219],{},"每个顶点的法线是相邻三个面的平均",[11,221,222],{},"应该用的就是UE4默认的那种方式，没有考究过。导入计算方面，对于脸部这种分成了好几个部件的情况，会统一导入，防止在边缘出现计算问题。",[11,224,225],{},[44,226],{"alt":46,"src":227},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fcompute-normal-merge-300x245.png",[11,229,230],{},"顶点颜色方面，使用情况如下：",[232,233,234,237,240],"blockquote",{},[11,235,236],{},"Red: Diffuse Offset",[11,238,239],{},"Blue: Ambient Occlusion",[11,241,242],{},"Green: DepthShift(轮廓线用)",[244,245,247],"h3",{"id":246},"basepass","BasePass",[11,249,250],{},"主要计算用于后面光照计算的必要数据：",[11,252,253],{},[44,254],{"alt":46,"src":255},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fbase-pass-300x124.png",[257,258,260],"h4",{"id":259},"basecolor与shadowcolor","BaseColor与ShadowColor",[11,262,263],{},"BaseColor为明亮处的颜色，ShadowColor为暗处的颜色。",[11,265,266],{},"ShadowColor基于BaseColor进行计算：",[11,268,269],{},[44,270],{"alt":46,"src":271},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fshadow-color-300x156.png",[11,273,274],{},"针对头发的颜色，BaseColor和ShadowColor都是直接在材质中指定颜色。如果头发是两种颜色的情况，会采用梯度图来进行过渡。头发的BaseColor和ShadowColor可以不依赖上面的规则进行设定。",[257,276,158],{"id":277},"ndotl",[11,279,280],{},"基于光源方向和ImportNormal进行计算：",[282,283,287],"pre",{"className":284,"code":285,"language":286,"meta":46,"style":46},"language-cpp shiki shiki-themes github-light-high-contrast github-dark monokai","HalfNoL = dot(Normal, Light) * 0.5 + 0.5;\nToonNoL = step(Threshold, HalfNoL);\n","cpp",[288,289,290,325],"code",{"__ignoreMap":46},[291,292,295,299,303,307,310,313,317,320,322],"span",{"class":293,"line":294},"line",1,[291,296,298],{"class":297},"s0idv","HalfNoL ",[291,300,302],{"class":301},"sLXdl","=",[291,304,306],{"class":305},"scAT4"," dot",[291,308,309],{"class":297},"(Normal, Light) ",[291,311,312],{"class":301},"*",[291,314,316],{"class":315},"stP0Q"," 0.5",[291,318,319],{"class":301}," +",[291,321,316],{"class":315},[291,323,324],{"class":297},";\n",[291,326,328,331,333,336],{"class":293,"line":327},2,[291,329,330],{"class":297},"ToonNoL ",[291,332,302],{"class":301},[291,334,335],{"class":305}," step",[291,337,338],{"class":297},"(Threshold, HalfNoL);\n",[11,340,341],{},"PPT中解释了为什么要在BasePass中进行NdotL，除了精度上的考虑之外，主要是对要将什么搭载到GBuffer上的设计考量。ImportNormal只会在这个步骤中用，在这里计算完之后就不需要在GBuffer中传递ImportNormal了。",[257,343,345],{"id":344},"specular","Specular",[11,347,348],{},"主要是材质中直接传过来的SpecularValue，以及计算SpecularMask。",[11,350,351],{},"SpecularMask用于表达主光源的高光，基于Blinn Phong反射模型进行：",[282,353,358],{"className":354,"code":356,"language":357},[355],"language-text","H = normalize(Light + View);\nNoH = dot(Normal,\nToonNoH = step(Threshold, NoH);\n","text",[288,359,356],{"__ignoreMap":46},[11,361,362],{},"头发的高光有另外的处理逻辑：",[11,364,365],{},[44,366],{"alt":46,"src":367},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fhair-specular-mask-300x134.png",[11,369,370],{},"似乎会根据头发本身与重心的距离以及其ID，与光源进行配合动态的缩放。",[244,372,373],{"id":373},"方向光",[11,375,376],{},"方向光的计算主要分为两个过程：",[211,378,379,382],{},[214,380,381],{},"使用NdotL(GBufferD.Alpha)来处理BaseColor和ShadowColor(ShadowColor作为Subsurface处理)",[214,383,384],{},"通过BaseColor的亮度结合SpecularValue以及SpecularMask计算高光",[11,386,387],{},[44,388],{"alt":46,"src":389},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fdirectional-light-300x164.png",[11,391,392],{},"使用的就是上面BasePass过程中生成的5张结果。",[244,394,395],{"id":395},"点光源",[11,397,398],{},"无视法线，直接按照与光源的距离来进行计算。",[11,400,401],{},[44,402],{"alt":46,"src":403},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fpoint-light-300x183.png",[11,405,406],{},"同时为Toon添加专用的参数来进行效果调节。",[244,408,409],{"id":409},"天光和间接光",[11,411,412],{},"将法线视为向上(0,0,1)来处理。",[11,414,415],{},[44,416],{"alt":46,"src":417},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fskylight-300x182.png",[11,419,406],{},[244,421,423],{"id":422},"rimlight","RimLight",[11,425,426],{},"在光照后添加了一个新的PostRimLight的Pass来处理RimLight计算。",[11,428,429],{},"计算使用RimLightWidth(GBufferE.Alpha)和RimLightMask(GBufferE.Green)来进行。",[11,431,432],{},"通过深度信息作Sobel filter计算",[11,434,435],{},[44,436],{"alt":46,"src":437},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Frim-light-depth-300x134.png",[11,439,440],{},"再通过RimLightMask屏蔽掉不希望的部分之后，叠加到Specular上。",[11,442,443],{},[44,444],{"alt":46,"src":445},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Frim-light-mask-300x127.png",[29,447,448],{"id":448},"轮廓线",[11,450,451],{},"轮廓线的绘制在PostOutlinePass中，这个Pass放在了光照和半透明计算之间，确保轮廓线能在水中等情况下受到折射的影响，同时保证半透明物体上不绘制轮廓线。",[11,453,454],{},"轮廓线主要有以下的计算来源：",[211,456,457,460,463,466],{},[214,458,459],{},"基于深度： sobel filter，比较深度并只在深度更大的地方绘制，通过depthshift限制一些地方(脸上)的绘制",[214,461,462],{},"基于ID：sobel filter，在值较大的上绘制",[214,464,465],{},"基于法线： 内积，主要针对深度差不多，ID也分不开的地方",[214,467,468],{},"预先绘制的：从G-Buffer中取出",[11,470,471],{},"使用到的资源包括之前的顶点颜色G通道中的DepthShift，以及额外的通过TexCoord1来获取的第二组顶点颜色。另外，有一张预先绘制的轮廓线贴图。",[11,473,474],{},"绘制过程主要使用GBufferF：",[11,476,477],{},[44,478],{"alt":46,"src":479},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fline-300x129.png",[11,481,482],{},"Red: 从TexCoord1.x中获取轮廓线粗细，并根据摄像距离和分辨率进行调整",[11,484,485],{},"Green: 从TexCoord1.y中获取轮廓线ID",[11,487,488],{},"Blue: 从预先绘制的贴图来绘制，同时使用轮廓模型防止绘制溢出",[11,490,491],{},"Alpha: 从VertexColor.g获取DepthShift，在基于深度计算时使用，绘制时基于参数进行反相",[244,493,494],{"id":494},"眉轮廓线",[11,496,497],{},"动画风格中常见的将眉毛绘制到前发之上的手法。",[11,499,500],{},"绘制上，将除头发以外的部分在BasePass中绘制，而头发单独在上面图中的ToonHairPass绘制。",[11,502,503],{},"眉毛的罗廓线由基于ID的方法生成，首先，在BasePass中生成一个眉毛本身的Mask，之后头发绘制时在更新轮廓用的ID的时候，在这个Mask上的部分不再更新。由此，在进行轮廓线生成的时候，就能够通过ID来产生眉毛的轮廓了。",[244,505,506],{"id":506},"抗锯齿",[11,508,509],{},"用上面的方法绘制的轮廓线会由于TAA导致模糊。",[11,511,512],{},"这里通过利用ResponsiveAA来解决，将轮廓线绘制到stencil来减轻轮廓线被TAA模糊的情况。",[29,514,515],{"id":515},"阴影",[244,517,519],{"id":518},"shadowmap","ShadowMap",[11,521,522],{},"对于本身的ShadowMap的逻辑改造，主要目的是移除Toon材质上的自阴影。",[11,524,525],{},"处理方式上，根据是否是Toon材质将ShadowMap分开处理，这样就可以在移除自阴影的同时接受背景物体的阴影。",[11,527,528],{},[44,529],{"alt":46,"src":530},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002FShadowMaps-284x300.png",[244,532,534],{"id":533},"offsetshadow","OffsetShadow",[11,536,537],{},"为了补偿由于没有自阴影导致的图像信息不足，添加了一个在屏幕空间进行偏移的阴影。",[11,539,540],{},"处理这个的是最上面图中的OffsetShadowDepth和OffsetShadowMerge。",[11,542,543],{},"过程的原理描述起来其实很简单，首先，在OffsetShadowDepth中，根据深度信息产生一个向下的偏移。",[11,545,546],{},[44,547],{"alt":46,"src":548},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002FOffset-Shadow-Depth--300x169.png",[11,550,551],{},"然后在OffsetShadowMerge中，根据深度对比将信息描绘到G-Buffer中的NdotL中去(GBufferD.Alpha)。",[11,553,554],{},[44,555],{"alt":46,"src":556},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002FOffset-Shadow-Merge-300x149.png",[29,558,560],{"id":559},"postprocess","PostProcess",[11,562,563],{},"后处理的方面比较简单，使用了柔光滤镜。在动画风格中经常使用到，虽然Bloom也能达到类似的效果，但还是另外的进行了实现。",[11,565,566],{},[44,567],{"alt":46,"src":568},"\u002Fwp-content\u002Fuploads\u002F2021\u002F11\u002Fpost-process-300x154.png",[232,570,571],{},[11,572,573,574,578],{},"柔光滤镜，应该和[",[18,575,577],{"href":576},"\u002F2021-01-24-ue4-fest-npr-note\u002F#i-10","这个","]是一样的。",[29,580,581],{"id":581},"总结",[11,583,584],{},"虽然改造引擎的方法可能不是很通用，但是其中的一些思路还是比较有参考作用的。对于动画风格渲染，需要注意的部分有很多共通的地方，不过由于UE4的渲染流程是写死的，所以不会像U3D之类的那样灵活。",[11,586,587],{},"PPT后面还有一些关于Actor的动态Instance相关的优化，由于不是很关注这块，所以没有详细的看过，这里就不记录了。",[589,590,591],"style",{},"html pre.shiki code .s0idv, html code.shiki .s0idv{--shiki-default:#0E1116;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .sLXdl, html code.shiki .sLXdl{--shiki-default:#A0111F;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .scAT4, html code.shiki .scAT4{--shiki-default:#622CBC;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .stP0Q, html code.shiki .stP0Q{--shiki-default:#023B95;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":46,"searchDepth":327,"depth":593,"links":594},3,[595,596,608,612,616,617],{"id":31,"depth":327,"text":31},{"id":195,"depth":327,"text":195,"children":597},[598,604,605,606,607],{"id":246,"depth":593,"text":247,"children":599},[600,602,603],{"id":259,"depth":601,"text":260},4,{"id":277,"depth":601,"text":158},{"id":344,"depth":601,"text":345},{"id":373,"depth":593,"text":373},{"id":395,"depth":593,"text":395},{"id":409,"depth":593,"text":409},{"id":422,"depth":593,"text":423},{"id":448,"depth":327,"text":448,"children":609},[610,611],{"id":494,"depth":593,"text":494},{"id":506,"depth":593,"text":506},{"id":515,"depth":327,"text":515,"children":613},[614,615],{"id":518,"depth":593,"text":519},{"id":533,"depth":593,"text":534},{"id":559,"depth":327,"text":560},{"id":581,"depth":327,"text":581},"2021-11-16","md",{"layout":621,"status":622,"published":623,"wordpress_id":624,"wordpress_url":625,"date_gmt":626,"excerpt":627},"post","publish",true,3557,"\u002F?p=3557","2021-11-15 17:18:59 +0000",{"type":8,"value":628},[629],[11,630,13],{},"\u002F2021-11-16-note-blue-protocol-anime-render",{"title":6,"description":13},"_legacy\u002F2021\u002F2021-11-16-note-blue-protocol-anime-render",[635,636],"UE4","CEDEC","k3Wf7wAjkskIvTzdrhF42jFwPhW8WAtbJHzIkxt3V-4",{"id":639,"title":640,"body":641,"date":618,"description":645,"extension":619,"meta":807,"navigation":623,"path":819,"seo":820,"stem":821,"tags":822,"__hash__":823},"blogs\u002F_legacy\u002F2021\u002F2021-11-16-note-of-video-by-jonastyroller.md","JonasTyroller游戏设计视频笔记",{"type":8,"value":642,"toc":798},[643,646,649,652,660,669,672,691,694,697,700,711,714,717,732,735,738,746,749,752,755,758,769,772,775,783,786,789,792,795],[11,644,645],{},"最近看了JonasTyroller的几个视频，感觉挺有收获，在此作些笔记。",[11,647,648],{},"这里仅作个人笔记，强烈推荐看原视频，每个人的视点不同可能会出现理解偏差。",[29,650,651],{"id":651},"如何获得游戏创意",[11,653,654,655],{},"原视频： ",[18,656,659],{"href":657,"rel":658},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=y2IAjXl5xRU",[22],"GET GAME IDEAS - The Ultimate Guide!",[11,661,662,663,668],{},"另有一个关于设计的视频：",[18,664,667],{"href":665,"rel":666},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=x46VdUtvDDs",[22],"Get Good Design Ideas! But... Design Method!","，介绍的思路具有相似性",[11,670,671],{},"对于任何游戏创意，需要从以下四点去考虑是否合适：",[211,673,674,677,685,688],{},[214,675,676],{},"它是否让你兴奋",[214,678,679,680,684],{},"它是否",[681,682,683],"strong",{},"对你","是可行的",[214,686,687],{},"它的目标玩家群体",[214,689,690],{},"它是否能然目标玩家兴奋",[11,692,693],{},"除了第一点之外，后面三点更多的是出于商业上的考量。",[11,695,696],{},"寻找游戏创意首先要从玩家所熟悉的游戏机制开始，要从头创造一个从未有过的游戏模式是很难的。现有的游戏机制已经为玩家所熟悉并获得了很多玩家的喜爱。",[11,698,699],{},"总结起来共有三个步骤：",[211,701,702,705,708],{},[214,703,704],{},"寻找现有的游戏机制",[214,706,707],{},"添加创新元素、核心机制",[214,709,710],{},"找出核心卖点，砍掉多余的部分",[11,712,713],{},"从第一步找到的机制中，尝试加入一些新的东西。设计新的挑战或者移除已有的机制，添加新的限定条件或者尝试打破常规。",[11,715,716],{},"可以通过分析现有的机制来找到新的想法：",[718,719,720,723,726,729],"ul",{},[214,721,722],{},"机制中玩家的障碍是什么",[214,724,725],{},"胜利条件\u002F失败条件",[214,727,728],{},"机制的核心是什么",[214,730,731],{},"玩家的决策和操作是如何进行的",[11,733,734],{},"至于砍掉多余的部分，就是要让游戏的总体设计向核心卖点靠拢。游戏最终的核心卖点一定要简单明快，要能形成一个简单的Pitch。",[29,736,737],{"id":737},"如何快速让游戏变得两倍有趣",[11,739,740,741],{},"原视频：",[18,742,745],{"href":743,"rel":744},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=plj09H-aLOk",[22],"How to make your game 2X as fun! - (In No Time.)",[11,747,748],{},"可能建议上不是很有什么新的内容，但是视频的例子举得很好。能够看到一个粗糙的游戏如何变得像一个游戏。",[244,750,751],{"id":751},"玩家决策设计",[11,753,754],{},"需要为玩家的决策空间设计最优解，但是不能太过于明晰",[11,756,757],{},"添加设计：",[718,759,760,763,766],{},[214,761,762],{},"使用重武器时移动速度下降，扔雷时不能移动",[214,764,765],{},"不射击时移动更快",[214,767,768],{},"场景中始终有两个得分点",[244,770,771],{"id":771},"主题颜色",[11,773,774],{},"对游戏内的物体，玩家、敌人、地形的颜色进行调整。",[11,776,777,778,782],{},"可以到网上找些现成的颜色方案，视频中的是[",[18,779,577],{"href":780,"rel":781},"https:\u002F\u002Fcoolors.co",[22],"]。",[244,784,785],{"id":785},"调整节奏",[11,787,788],{},"尽可能让游戏的节奏变快。",[244,790,791],{"id":791},"特效",[11,793,794],{},"添加与环境和上下文关联的特效。",[11,796,797],{},"添加相机移动(CameraShake)效果。",{"title":46,"searchDepth":327,"depth":593,"links":799},[800,801],{"id":651,"depth":327,"text":651},{"id":737,"depth":327,"text":737,"children":802},[803,804,805,806],{"id":751,"depth":593,"text":751},{"id":771,"depth":593,"text":771},{"id":785,"depth":593,"text":785},{"id":791,"depth":593,"text":791},{"layout":621,"status":622,"published":623,"author":808,"author_login":809,"author_email":810,"author_url":811,"wordpress_id":812,"wordpress_url":813,"date_gmt":814,"excerpt":815},{"display_name":809,"login":809,"email":810,"url":811},"chaoshikari","chaoshikari@gmail.com","\u002F",3618,"\u002F?p=3618","2021-11-15 18:29:08 +0000",{"type":8,"value":816},[817],[11,818,645],{},"\u002F2021-11-16-note-of-video-by-jonastyroller",{"title":640,"description":645},"_legacy\u002F2021\u002F2021-11-16-note-of-video-by-jonastyroller",[],"XidshZQU1BSEv9APN3A_k3b1btlrsPey40Ujo_vQMrI",{"id":825,"title":826,"body":827,"date":1071,"description":831,"extension":619,"meta":1072,"navigation":623,"path":1081,"seo":1082,"stem":1083,"tags":1084,"__hash__":1085},"blogs\u002F_legacy\u002F2021\u002F2021-05-31-elasticsearch-elk-install-and-use-note.md","Elasticsearch环境安装使用笔记",{"type":8,"value":828,"toc":1062},[829,832,835,838,842,851,854,857,865,868,871,874,880,883,889,896,899,905,909,912,921,924,929,932,938,947,950,955,959,967,973,976,979,988,991,996,999,1005,1009,1012,1018,1021,1027,1030,1036,1039,1047,1050,1056,1059],[11,830,831],{},"想要作一些简单的数据分析，看kibana界面似乎很好用的样子，于是尝试安装了一下。",[11,833,834],{},"实际操作环境：windows10和ubuntu20.04。",[11,836,837],{},"本次的目标是，通过ELK框架对在Mysql中的数据进行分析。",[29,839,841],{"id":840},"elk安装","ELK安装",[11,843,844,845,850],{},"基础的安装部分比较简单，可以参考网上的其他文章。Ubuntu系统上的安装可以参考DigitalOcean的",[18,846,849],{"href":847,"rel":848},"https:\u002F\u002Fwww.digitalocean.com\u002Fcommunity\u002Ftutorials\u002Fhow-to-install-elasticsearch-logstash-and-kibana-elastic-stack-on-ubuntu-20-04",[22],"这篇","，基本上就是使用apt命令就可以了。windows上的就更加单纯一些，只要下载对应的bat文件然后解压就可以了。",[11,852,853],{},"不过由于很多工具都是linux上的，而且windows安装jdk的mysql包之类的会很麻烦，最后切换到ubuntu上了。",[11,855,856],{},"另外，如果是在云服务器上进行安装的话，需要注意主机的配置。实际测试，在腾讯云上这个配置：",[232,858,859,862],{},[11,860,861],{},"1核 2GB 1Mbps",[11,863,864],{},"系统盘：高性能云硬盘",[11,866,867],{},"无法正常的运行，只要一启动logstash，就会导致Elasticsearch挂掉。",[244,869,870],{"id":870},"指令备忘",[11,872,873],{},"使用时注意开启服务：",[282,875,878],{"className":876,"code":877,"language":357},[355],"sudo systemctl start elasticsearch\nsudo systemctl start kibana\n",[288,879,877],{"__ignoreMap":46},[11,881,882],{},"可以使用下面的指令检查elasticsearch是否成功开启",[282,884,887],{"className":885,"code":886,"language":357},[355],"curl -X GET \"localhost:9200\"\n",[288,888,886],{"__ignoreMap":46},[11,890,891,892,895],{},"kibana直接在本地访问就好：",[288,893,894],{},"http:\u002F\u002Flocalhost:5601","。如果是在服务器上可能需要额外的配置端口开放或者用nginx作反向代理，可以参考上面的digitalocean的文档进行。",[11,897,898],{},"如果需要服务常驻的话可以打开：",[282,900,903],{"className":901,"code":902,"language":357},[355],"sudo systemctl enable elasticsearch\nsudo systemctl enable kibana\n",[288,904,902],{"__ignoreMap":46},[29,906,908],{"id":907},"logstash使用","Logstash使用",[11,910,911],{},"ELK里面，其实Elasticsearch和Kibana一开始只需要安装好就可以了，主要的是Logstash配置好，将数据推送过去。",[11,913,914,915,920],{},"LogStash其实官方的这篇就很好用了：",[18,916,919],{"href":917,"rel":918},"https:\u002F\u002Fwww.elastic.co\u002Fguide\u002Fen\u002Flogstash\u002F7.12\u002Ffirst-event.html",[22],"Stashing Your First Event","。",[11,922,923],{},"这边稍微记录下有用的内容，对于找不到LogStash的路径的话，可以使用指令",[232,925,926],{},[11,927,928],{},"whereis logstash",[11,930,931],{},"logstash最基本的功能测试，可以用于检查功能是否正常",[282,933,936],{"className":934,"code":935,"language":357},[355],"cd logstash-7.12.1\nbin\u002Flogstash -e 'input { stdin { } } output { stdout {} }'\n",[288,937,935],{"__ignoreMap":46},[11,939,940,941,946],{},"官方有个",[18,942,945],{"href":943,"rel":944},"https:\u002F\u002Fwww.elastic.co\u002Fguide\u002Fen\u002Flogstash\u002F7.12\u002Fadvanced-pipeline.html",[22],"FileBeat教程","，使用logstash接入filebeat来分析apache的日志，基本照着做就可以了。",[11,948,949],{},"要测试一个配置文件是否正确：",[232,951,952],{},[11,953,954],{},"sudo bin\u002Flogstash --config.test_and_exit -f first.conf",[244,956,958],{"id":957},"mysql连接","Mysql连接",[11,960,961,962,920],{},"Logstash使用mysql需要额外的安装Mysql连接用套件。windows版可以在这里下载",[18,963,966],{"href":964,"rel":965},"https:\u002F\u002Fdev.mysql.com\u002Fdownloads\u002Fconnector\u002Fj\u002F",[22],"连接套件",[282,968,971],{"className":969,"code":970,"language":357},[355],"input {\n  jdbc {\n    jdbc_driver_library => \"C:\u002F\u002FProgram Files (x86)\u002F\u002FMySQL\u002F\u002FConnector J 8.0\u002F\u002Fmysql-connector-java-8.0.25.jar\"\n    jdbc_driver_class => \"com.mysql.cj.jdbc.Driver\"\n    jdbc_connection_string =>  \"jdbc:mysql:\u002F\u002Flocalhost:3306\u002Fawesome_database\"\n    jdbc_user => \"your_awesome_mysql_user\"\n    jdbc_password =>  \"your_awesome_password\"\n    statement =>  \"SELECT * from awseome_table_name\"\n  }\n}\n\noutput{\n  stdout { codec => rubydebug { metadata => true } }\n}\n\n\n",[288,972,970],{"__ignoreMap":46},[11,974,975],{},"jdbc_driver_library 这里需要指向安装目录中对应的连接套件的jar文件。其他的配置项都比较明了。",[11,977,978],{},"ubuntu下也需要对应的安装一些连接套件：",[232,980,981],{},[11,982,983,984],{},"wget ",[18,985,986],{"href":986,"rel":987},"https:\u002F\u002Fdev.mysql.com\u002Fget\u002FDownloads\u002FConnector-J\u002Fmysql-connector-java-5.1.46.zip",[22],[11,989,990],{},"然后解压",[232,992,993],{},[11,994,995],{},"unzip mysql-connector-java-5.1.46.zip",[11,997,998],{},"将解压后内部的jar路径填到logstash配置的input部分",[282,1000,1003],{"className":1001,"code":1002,"language":357},[355],"input {\n  jdbc {\n    jdbc_driver_library => \"usr\u002Fshare\u002Flogstash\u002Fmysql-connector-java-5.1.46\u002Fmysql-connector-java-5.1.46.jar\"\n    jdbc_driver_class =>\"com.mysql.jdbc.Driver\"\n    jdbc_connection_string => \"jdbc:mysql:\u002F\u002Flocalhost:3306\u002Fawesome_database_name\"\n    jdbc_user => \"your_awesome_mysql_user\"\n    jdbc_password => \"your_awesome_password\"\n    statement =>  \"SELECT * from awseome_table_name\"\n  }\n}\n\noutput{\n  stdout { codec => rubydebug { metadata => true } }\n}\n\n",[288,1004,1002],{"__ignoreMap":46},[244,1006,1008],{"id":1007},"输入到elastic","输入到Elastic",[11,1010,1011],{},"需要将配置中的output改到对应的elastic",[282,1013,1016],{"className":1014,"code":1015,"language":357},[355],"output{\n  # stdout { codec => rubydebug { metadata => true } }\n  elasticsearch {\n        hosts => [ \"localhost:9200\" ]\n        index => \"kibana-index\"\n    }\n   # stdout { codec => dots }\n}\n\n",[288,1017,1015],{"__ignoreMap":46},[11,1019,1020],{},"加入到index后，kibana中需要创建新的index parten才能正确看到。",[11,1022,1023,1024],{},"如果是本地的话，使用这个网址就可以了：",[288,1025,1026],{},"http:\u002F\u002Flocalhost:5601\u002Fapp\u002Fmanagement\u002Fkibana\u002FindexPatterns\u002F",[11,1028,1029],{},"Mysql推送数据多次推送的情况下，会出现很多重复的数据，可以通过在output这边添加配置解决：",[282,1031,1034],{"className":1032,"code":1033,"language":357},[355],"output{\n  #stdout { codec => rubydebug { metadata => true } }\n  elasticsearch {\n       hosts => [ \"localhost:9200\" ]\n       index => \"productinfo\"\n       document_id => \"%{productid}\"\n  }\n   # stdout { codec => dots }\n}\n\n",[288,1035,1033],{"__ignoreMap":46},[11,1037,1038],{},"其中，productid就是mysql这边的主键。",[11,1040,1041,1042,782],{},"对于更加复杂的情况，可以在input这边处理，比较简单的处理方式是使用jdbc input提供的预定义配合时间戳来进行。详细的可以看[",[18,1043,1046],{"href":1044,"rel":1045},"https:\u002F\u002Fwww.elastic.co\u002Fguide\u002Fen\u002Flogstash\u002Fcurrent\u002Fplugins-inputs-jdbc.html#_predefined_parameters",[22],"官方文档",[11,1048,1049],{},"比较简单的用法：",[282,1051,1054],{"className":1052,"code":1053,"language":357},[355],"input {\n  jdbc {\n    statement => \"SELECT id, mycolumn1, mycolumn2 FROM my_table WHERE id > :sql_last_value\"\n    use_column_value => true\n    tracking_column => \"id\"\n    # ... other configuration bits\n  }\n}\n",[288,1055,1053],{"__ignoreMap":46},[11,1057,1058],{},"这边用的是id来避免input的时候重复，如果ID不是自增的，可以用timestamp来代替，其实默认sql_last_value就是时间戳形式的。更加复杂的情况需要结合last_run来进行，可以进一步参考文档。",[11,1060,1061],{},"一时半会没看到自动根据主键来避免重复的input配置方式。这边暂时在output那边控制下就可以了，不纠结了。",{"title":46,"searchDepth":327,"depth":593,"links":1063},[1064,1067],{"id":840,"depth":327,"text":841,"children":1065},[1066],{"id":870,"depth":593,"text":870},{"id":907,"depth":327,"text":908,"children":1068},[1069,1070],{"id":957,"depth":593,"text":958},{"id":1007,"depth":593,"text":1008},"2021-05-31",{"layout":621,"status":622,"published":623,"author":1073,"author_login":809,"author_email":810,"author_url":811,"wordpress_id":1074,"wordpress_url":1075,"date_gmt":1076,"excerpt":1077},{"display_name":809,"login":809,"email":810,"url":811},3517,"\u002F?p=3517","2021-05-31 06:37:33 +0000",{"type":8,"value":1078},[1079],[11,1080,831],{},"\u002F2021-05-31-elasticsearch-elk-install-and-use-note",{"title":826,"description":831},"_legacy\u002F2021\u002F2021-05-31-elasticsearch-elk-install-and-use-note",[],"lqqeLbcbNY6obK0Fc0mXsCRffnJ_YCxUAk-E2sF8g8M",{"id":1087,"title":1088,"body":1089,"date":1194,"description":1093,"extension":619,"meta":1195,"navigation":623,"path":1204,"seo":1205,"stem":1206,"tags":1207,"__hash__":1208},"blogs\u002F_legacy\u002F2021\u002F2021-05-30-ue4-add-new-asset-type.md","UE4新增Asset类型",{"type":8,"value":1090,"toc":1189},[1091,1094,1097,1100,1104,1107,1110,1116,1119,1122,1128,1131,1137,1140,1146,1150,1153,1163,1169,1172,1178,1181,1183,1186],[11,1092,1093],{},"想要在编辑器里面添加新的Asset类型，算是比较常见又不是经常使用的需求。",[11,1095,1096],{},"当前UE4版本为4.26.2。",[11,1098,1099],{},"虽然本身并不复杂，但是每次添加的时候如果项目中没有相关的代码，就又要去查一次。不然总是会在一些奇怪的地方卡住导致在编辑器中无法使用的情况。因此在这里记录一下，方便下次查找。",[29,1101,1103],{"id":1102},"ufactory继承","UFactory继承",[11,1105,1106],{},"首先需要继承UFactory，直接用UE4添加类，或者自己写即可。",[11,1108,1109],{},"覆盖掉下面的函数：",[282,1111,1114],{"className":1112,"code":1113,"language":357},[355],"virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;\nvirtual bool ShouldShowInNewMenu() const override;\n",[288,1115,1113],{"__ignoreMap":46},[11,1117,1118],{},"ShouldShowInNewMenu直接返回True则可用在编辑器的右键新建的菜单中出现。",[11,1120,1121],{},"FactoryCreateNew则直接新建一个对应类型的Object即可：",[282,1123,1126],{"className":1124,"code":1125,"language":357},[355],"return NewObject\u003CUMyObjectType>(InParent, InClass, InName, Flags);\n",[288,1127,1125],{"__ignoreMap":46},[11,1129,1130],{},"同时需要在构造函数中初始化一些设置",[282,1132,1135],{"className":1133,"code":1134,"language":357},[355],"\nSupportedClass = UMyObjectType::StaticClass();\nbCreateNew = true;\nbEditAfterNew = true;\n\n",[288,1136,1134],{"__ignoreMap":46},[11,1138,1139],{},"如果遇到了一些奇怪的链接问题，可以先检查下是否在模块的build.cs中包含了UnrealEd。",[282,1141,1144],{"className":1142,"code":1143,"language":357},[355],"PrivateDependencyModuleNames.AddRange(\n  new string[]\n  {\n    \"CoreUObject\",\n    \"Engine\",\n    \"UnrealEd\",\n  }\n);\n\n",[288,1145,1143],{"__ignoreMap":46},[29,1147,1149],{"id":1148},"assettypeactions","AssetTypeActions",[11,1151,1152],{},"之前的版本有上面的操作就可以了，但是目前4.26的版本需要额外的操作才能在编辑器中看到新建按钮。",[11,1154,1155,1156,1159,1160,920],{},"定义一个新的类继承自",[288,1157,1158],{},"FAssetTypeActions_Base","，一个比较好的参考是",[288,1161,1162],{},"FAssetTypeActions_DataAsset",[282,1164,1167],{"className":1165,"code":1166,"language":357},[355],"class FAssetTypeActions_DataAsset : public FAssetTypeActions_Base\n{\npublic:\n  \u002F\u002F IAssetTypeActions Implementation\n  virtual FText GetName() const override { return NSLOCTEXT(\"AssetTypeActions\", \"AssetTypeActions_DataAsset\", \"Data Asset\"); }\n  virtual FColor GetTypeColor() const override { return FColor(201, 29, 85); }\n  virtual UClass* GetSupportedClass() const override { return UDataAsset::StaticClass(); }\n  virtual uint32 GetCategories() override { return EAssetTypeCategories::Misc; }\n};\n",[288,1168,1166],{"__ignoreMap":46},[11,1170,1171],{},"无需额外的定义，之后在Module的Start的地方进行注册即可",[282,1173,1176],{"className":1174,"code":1175,"language":357},[355],"void FAwesomeEdModule::StartupModule()\n{\n    IAssetTools& AssetTools = FModuleManager::LoadModuleChecked\u003CFAssetToolsModule>(\"AssetTools\").Get();\n    AssetTools.RegisterAssetTypeActions(MakeShareable(new FAwesomeAssetAction));\n}\n",[288,1177,1175],{"__ignoreMap":46},[11,1179,1180],{},"其中FAwesomeAssetAction就是新定义的AssetTypeAction。",[29,1182,581],{"id":581},[11,1184,1185],{},"由于这种功能一般加一次就会不管了，下一次在别的地方要用到的时候才发现API已经变了。由于本身操作不是很熟悉，很难把握是自己操作有误还是其他因素引起的。",[11,1187,1188],{},"总之这边记录的是当前版本的可行操作，之后的版本不行的话就可以判断可能是API有改变。",{"title":46,"searchDepth":327,"depth":593,"links":1190},[1191,1192,1193],{"id":1102,"depth":327,"text":1103},{"id":1148,"depth":327,"text":1149},{"id":581,"depth":327,"text":581},"2021-05-30",{"layout":621,"status":622,"published":623,"author":1196,"author_login":809,"author_email":810,"author_url":811,"wordpress_id":1197,"wordpress_url":1198,"date_gmt":1199,"excerpt":1200},{"display_name":809,"login":809,"email":810,"url":811},3508,"\u002F?p=3508","2021-05-30 13:15:20 +0000",{"type":8,"value":1201},[1202],[11,1203,1093],{},"\u002F2021-05-30-ue4-add-new-asset-type",{"title":1088,"description":1093},"_legacy\u002F2021\u002F2021-05-30-ue4-add-new-asset-type",[635],"LHeIYSAW7BjCYwrB_XV063tftDjPQLdpuY96Lu-v7xc",{"id":1210,"title":1211,"body":1212,"date":1839,"description":1216,"extension":619,"meta":1840,"navigation":623,"path":1849,"seo":1850,"stem":1851,"tags":1852,"__hash__":1853},"blogs\u002F_legacy\u002F2021\u002F2021-02-08-cedec2020-new-sakura-wars-note.md","CEDEC2020-新樱花大战笔记",{"type":8,"value":1213,"toc":1811},[1214,1217,1224,1227,1230,1233,1236,1244,1247,1252,1255,1275,1278,1281,1288,1293,1296,1301,1304,1307,1313,1316,1319,1322,1325,1328,1331,1334,1337,1340,1347,1350,1353,1356,1359,1362,1382,1385,1390,1393,1396,1399,1402,1406,1409,1412,1415,1419,1422,1425,1428,1431,1434,1439,1442,1445,1448,1451,1454,1468,1471,1474,1477,1480,1485,1488,1493,1496,1501,1504,1507,1518,1523,1527,1530,1533,1538,1541,1544,1547,1550,1553,1556,1559,1562,1565,1570,1573,1584,1587,1590,1593,1598,1601,1604,1607,1612,1615,1618,1621,1624,1629,1632,1635,1638,1641,1644,1647,1650,1655,1658,1663,1666,1669,1674,1677,1680,1683,1688,1691,1694,1697,1700,1703,1706,1709,1712,1717,1720,1723,1726,1729,1734,1737,1740,1743,1746,1749,1754,1757,1760,1763,1765,1768,1771,1776,1779,1782,1785,1788,1808],[11,1215,1216],{},"这个是CEDEC2020上《新樱花大战》的分享的笔记。",[11,1218,1219,1220],{},"CEDEC：",[18,1221,1222],{"href":1222,"rel":1223},"https:\u002F\u002Fcedil.cesa.or.jp\u002Fcedil_sessions\u002Fview\u002F2328",[22],[11,1225,1226],{},"《新樱花大战》是PS4上的游戏，开发是世嘉。分享的主要内容是关于角色制作与渲染。",[11,1228,1229],{},"开发时间为2016年到2019年。",[29,1231,1232],{"id":1232},"概要",[11,1234,1235],{},"《新樱花大战》在初期开发的时候使用的是双色调的Toon渲染风格，没有使用PBR。",[11,1237,1238,1239,1243],{},"后面根据用户的诉求，将渲染的方向转向了具有Toon风格的PBR，在制作途中切换到了支持PBR的",[1240,1241,1242],"em",{},"Hedgehog Engine 2","引擎。",[11,1245,1246],{},"在做渲染切换时，团队制作了一个用于后续渲染质量参照的场景。在这个场景中对各种要素进行测试，之后所有的场景制作都要求达到同等的水平。",[11,1248,1249],{},[44,1250],{"alt":46,"src":1251},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206104804946-300x166.png",[11,1253,1254],{},"场景中包含的要素有：",[718,1256,1257,1260,1263,1266,1269,1272],{},[214,1258,1259],{},"基于物理模拟的头发和衣服表现",[214,1261,1262],{},"丰富的表情动画",[214,1264,1265],{},"视线自动朝向摄像",[214,1267,1268],{},"根据摄像角度不同的瞳孔高光位置调整",[214,1270,1271],{},"头发上天使之轮的移动",[214,1273,1274],{},"眼睛高光的湿润感",[29,1276,1277],{"id":1277},"模型制作",[11,1279,1280],{},"在谈到建模之前首先介绍了下角色设计上的一些变化。",[11,1282,1283,1284,1287],{},"女主角天宮櫻原本的设定更加强调开朗和柔和的部分，但是本作更加强调",[1240,1285,1286],{},"女剑士","这一角色特征。所以在对制作的几个调整过的版本进行对比后，最终采用了基本表情时是凛然的剑士，而在其他如笑容的表情时则体现出女性的柔和感的形象设计。",[11,1289,1290],{},[44,1291],{"alt":46,"src":1292},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206110013039-300x131.png",[11,1294,1295],{},"在角色的设计方案确定之后，以2D的角色设计为基础，进行3D建模。在模型制作过程中，对原画的色彩分布和特征进行尽可能的重现。模型制作上，身体部分采用通用的体型模型，然后在其上制作服装来完成。",[11,1297,1298],{},[44,1299],{"alt":46,"src":1300},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206110444721-300x120.png",[244,1302,1303],{"id":1303},"制作流程",[11,1305,1306],{},"模型制作时使用的建模工具有：Maya、ZBrush、SbustancePainter。",[11,1308,1309,1312],{},[44,1310],{"alt":46,"src":1311},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206111906655-300x111.png","\n首先使用Maya制作出运行时使用的模型，然后在ZBrush中雕刻出表面的褶皱和详细的凹凸并烘培出法线贴图。",[11,1314,1315],{},"衣服的布料质感则是在SubstancePaiter中制作，最终在PhotoShop中合成到贴图中。",[11,1317,1318],{},"《新樱花大战》中，表情动画也是由角色制作负责的，所以在进行蒙皮权重后会进行Facia动画的制作。",[11,1320,1321],{},"最终，在Maya中导出模型、骨骼、表情动画以及材质动画，在引擎中对物理驱动的头发等摆动进行调整，就完成了角色的建模。",[244,1323,1324],{"id":1324},"模型规格",[11,1326,1327],{},"模型的规格上，面数在40000~65000区间，根据角色设计的不同会有区别。",[11,1329,1330],{},"由于游戏制作的目标帧率是30帧，而同屏角色最多时会有14个，因此逆向计算出角色的面数上限。",[11,1332,1333],{},"骨骼数量根据角色会有不同，天宮櫻的是最多的，有350根。",[244,1335,1336],{"id":1336},"通用骨骼",[11,1338,1339],{},"本作为了方便让动画可以在各个角色之间通用，基础的身体部分采用的是统一的骨骼构造。",[11,1341,1342,1343,1346],{},"通用的骨骼模型被称为",[1240,1344,1345],{},"Regulation Model","， 在此基础上对各个角色进行制作。",[11,1348,1349],{},"模型的体型，按照男性和女性，各自有S\u002FM\u002FL三种。",[11,1351,1352],{},"每个角色在制作时，首先决定采用哪一个体格，然后依此进行骨骼构造。各个角色在S\u002FM\u002FL的规格之外的身高差，则通过在实际运行中调整Root骨骼的缩放来实现。",[11,1354,1355],{},"动画制作时，统一使用M体型来制作通用动画，然后通过Retarget应用到各个体型上。",[244,1357,1358],{"id":1358},"可动饰物分类",[11,1360,1361],{},"对通用模型上的可动饰物进行分类：",[718,1363,1364,1367,1370,1373,1376,1379],{},[214,1365,1366],{},"长发",[214,1368,1369],{},"和服袖子",[214,1371,1372],{},"袴",[214,1374,1375],{},"长披风",[214,1377,1378],{},"被称为萌袖的大袖口",[214,1380,1381],{},"迷你裙",[11,1383,1384],{},"总共分为六个种类，由于很多和服相关的，直接看图比较明确：",[11,1386,1387],{},[44,1388],{"alt":46,"src":1389},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206114416868-300x245.png",[11,1391,1392],{},"在制作通用动画时，必须保证不会与这六个分类的可动饰物出现穿模。",[11,1394,1395],{},"而模型制作这边，则不可以在制作服装时与分类中的饰物出现太大的形状偏差。",[11,1397,1398],{},"这样，在两边都遵循标准的情况下，就可以很好的避免实际运行过程中对可动饰物进行物理模拟时出现穿模。",[11,1400,1401],{},"当然，在标准模型上的可动饰物之外，每个角色都会有一些自有的特色物品，但是这些物品，例如头发都会有自己的骨骼。",[244,1403,1405],{"id":1404},"zbrush高模制作","ZBrush高模制作",[11,1407,1408],{},"ZBrush中制作高模时，主要对衣服的褶皱和一些细节部分进行雕刻。",[11,1410,1411],{},"服装制作虽然有考虑过使用Marvelous Designer，但是由于本次是以动画的风格为标准的，所以最终没有采用。",[11,1413,1414],{},"褶皱这些的雕刻，如果过于接近现实感就会变得过于详细。于是参考了通常手办的制作方式，对过于详细的细节部分进行省略。",[244,1416,1418],{"id":1417},"substance","Substance",[11,1420,1421],{},"SubstancePaiter被用于制作衣服的布料质感、金属感，通过Substance导出，然后在PhotoShop中进行合成。导入到引擎后发现有一些需要调整的地方会进行一些微调。",[11,1423,1424],{},"在Substance中进行制作时，作为基础贴图烘培的AO贴图以及ZBrush输出的高模烘培这些都能够起到作用。",[29,1426,1427],{"id":1427},"贴图",[244,1429,1430],{"id":1430},"衣服贴图",[11,1432,1433],{},"首先是衣服部分的贴图：",[11,1435,1436],{},[44,1437],{"alt":46,"src":1438},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206120629412-300x140.png",[11,1440,1441],{},"Albedo:Alpha这张贴图的作用，是用来在Albedo上添加一个渐进的Alpha下降，避免内部的形状出现过度的发光。",[11,1443,1444],{},"上边中间的ID的贴图，是用于给衣服绘制轮廓线的。在颜色不同的边界线上，就会在PostEffect中绘制这种贴图中对应的颜色。而ID:Alpha则用于控制轮廓线的消失和混合。",[11,1446,1447],{},"《新樱花大战》的角色有两种轮廓线，一种是使用上面提到的ID贴图进行绘制的衣服的轮廓线，一种是通过背面法将身体模型反向扩展的通用的轮廓线。",[11,1449,1450],{},"法线贴图部分是通用的切线空间的法线贴图。",[11,1452,1453],{},"左下的Param贴图，是用于PBR的参数的",[718,1455,1456,1459,1462,1465],{},[214,1457,1458],{},"R通道是reflectance，用于指定材质的反射率",[214,1460,1461],{},"G通道是Smoothness，用于指定材质的光滑度",[214,1463,1464],{},"B通道是Metalness，用于指定材质的金属质感",[214,1466,1467],{},"A通道是AO贴图",[11,1469,1470],{},"这个贴图中，G通道的光滑度与UnrealEngine相比在0和1的作用上是反过来的。",[11,1472,1473],{},"右下角的CDRF贴图，用于调整阴影的色调，之后会说明。",[244,1475,1476],{"id":1476},"脸部贴图",[11,1478,1479],{},"接下来是脸部贴图：",[11,1481,1482],{},[44,1483],{"alt":46,"src":1484},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206122229817-300x150.png",[11,1486,1487],{},"基本上和衣服的贴图用法差不多，不同的部分是，脸部没有使用法线贴图，而Albedo:Apha则于衣服的黑白部分的作用是相反的。这种相反的作用，是为了让脸部特定的部位更加的发光，所以和衣服的表现目的不同。",[11,1489,1490],{},[44,1491],{"alt":46,"src":1492},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206122456725-300x124.png",[11,1494,1495],{},"脸部没有法线贴图，而是依然保留了采用PBR渲染之前的ToonShader风格的特殊法线调整。",[11,1497,1498],{},[44,1499],{"alt":46,"src":1500},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206123545782-150x150.png",[244,1502,1503],{"id":1503},"顶点颜色",[11,1505,1506],{},"本作有使用VertexColor，顶点颜色按照RGB通道控制不同的效果：",[718,1508,1509,1512,1515],{},[214,1510,1511],{},"R：控制背面法产生的轮廓线的粗细",[214,1513,1514],{},"G：作为曲率值使用(曲率越高说明形状越薄)，作用在之后解说，会与前面的CDRF贴图并用",[214,1516,1517],{},"B：比较特殊，是ScreenBaseShadow的Mask，用于防止在一些高特效的场景中的阴影溢出而使用",[11,1519,1520],{},[44,1521],{"alt":46,"src":1522},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206123623186-300x127.png",[244,1524,1526],{"id":1525},"crdf","CRDF",[11,1528,1529],{},"本作采用了会将曲率考虑在内的RampTexture。",[11,1531,1532],{},"RampTexture是指，根据光照的反向和法线的角度来对光照计算进行影响的贴图。",[11,1534,1535],{},[44,1536],{"alt":46,"src":1537},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206124017405-300x142.png",[11,1539,1540],{},"使用方法图中左上有提到：",[11,1542,1543],{},"R=法线和光照反向的内积",[11,1545,1546],{},"G=曲率",[11,1548,1549],{},"最右边代表光照与法线反向相同时的颜色，而最左边指定的是最暗时的颜色。",[11,1551,1552],{},"曲率是在上面的顶点颜色的G中指定的。",[11,1554,1555],{},"会添加曲率这个维度，是为了模拟Subsurface Scattering一样的表现，所以分为上下两段的RampTexture并用曲率在上面采样。顶点颜色的G值越高，则会采样出更加明显的阴影边界，而G值低时则会取出更加平滑的阴影。",[11,1557,1558],{},"根据部位的不同，会采用不同的RampTexture设计。例如，皮肤的RampTexture在设计上会让皮肤的阴影更加平滑。",[244,1560,1561],{"id":1561},"头发贴图",[11,1563,1564],{},"头发贴图使用除了天使光环的部分，基本和衣服相同",[11,1566,1567],{},[44,1568],{"alt":46,"src":1569},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206131038911-300x130.png",[11,1571,1572],{},"天使光环是头发上的那一圈高光。根据通道控制不同的属性",[718,1574,1575,1578,1581],{},[214,1576,1577],{},"R：光环的形状",[214,1579,1580],{},"G：添加到整体上的水平方向的Mask",[214,1582,1583],{},"B：在头发UV上的Mask",[11,1585,1586],{},"根据摄像的移动，会控制头发上的天使光环的移动。在竖直反向移动时，高光会有较大的移动量；而在水平反向上移动时，移动量就会很小。",[244,1588,1589],{"id":1589},"前发透过",[11,1591,1592],{},"对于一部分前发会挡住眉毛和眼睛的情况，会对前发使用特殊的Shader。",[11,1594,1595],{},[44,1596],{"alt":46,"src":1597},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206131558775-300x153.png",[11,1599,1600],{},"在单独的Buffer中进行眉毛的绘制，并在后处理时进行合成，让眉毛和眼睛产生一种透视感。在渲染时如果由于摄像角度导致前发会挡住眼睛时，会进行这个计算。",[244,1602,1603],{"id":1603},"瞳孔表现",[11,1605,1606],{},"本作的眼睛分为三个部分，眼白的部分是一个内凹的形状，瞳孔的部分则分为了向内凹的部分和向外突出的部分。",[11,1608,1609],{},[44,1610],{"alt":46,"src":1611},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206133019305-300x273.png",[11,1613,1614],{},"瞳孔的高光叠加在其突出的半透明部分上。根据摄像角度的不同，各个部分的贴图会分开进行移动，使得在哪个角度高光都能处在一个刚好的位置。",[11,1616,1617],{},"控制上，有针对高光上下左右的移动强度与控制移动范围的参数共同作用。",[244,1619,1620],{"id":1620},"眼睛的贴图",[11,1622,1623],{},"眼睛的贴图部分比较特殊：",[11,1625,1626],{},[44,1627],{"alt":46,"src":1628},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206133059919-300x68.png",[11,1630,1631],{},"从左边开始，是作为基础颜色的Albedo，以及用于Mask瞳孔的Alpha贴图，用于在Albedo上进行叠加的spt贴图，以及两张瞳孔高光，以及反应环境的matcapture贴图。",[11,1633,1634],{},"虽然很多动画风格的渲染中会省略掉瞳孔中的虹彩部分，但是本作为了提高角色靠近时的效果，进行了详细的绘制，同时为了体现环境的变化与matcap的贴图进行叠加。",[11,1636,1637],{},"高光贴图有两张，分别使用不同的UV动画进行控制，用于表现眼睛的湿润感。虽然是很细微的操作，但是对于表现角色的感情非常的有用。",[29,1639,1640],{"id":1640},"角色光照",[11,1642,1643],{},"角色光照主要依赖于在场景中的直射光以及烘培到GI中的光照缓存，GI中会有间接光缓存。",[11,1645,1646],{},"GI中能够烘培场景的一些细节光照信息，可以让角色在场景中的渲染变得更真实。",[11,1648,1649],{},"对于部分完全位于阴影中的场景，会在角色身上挂一个胶囊体的碰撞，根据其遮蔽信息，脚下投下阴影。",[11,1651,1652],{},[44,1653],{"alt":46,"src":1654},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206154842108-285x300.png",[11,1656,1657],{},"由于战斗中有大量的敌人，会相应的只在脚边进行操作。",[232,1659,1660],{},[11,1661,1662],{},"这个应该是通过Overlap信息在周围投射假的阴影",[244,1664,1665],{"id":1665},"背光灯",[11,1667,1668],{},"会在主光照的180度相反位置和左右90度的位置添加用于角色亮度修正的背光灯。",[11,1670,1671],{},[44,1672],{"alt":46,"src":1673},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206153934823-300x102.png",[11,1675,1676],{},"主要用于防止角色站在逆光的位置导致变暗的问题，以及背光灯的光照也可以强调出材质的质感。",[244,1678,1679],{"id":1679},"阴影修正",[11,1681,1682],{},"本作在光照中，对角色专门进行了光照的调整。",[11,1684,1685],{},[44,1686],{"alt":46,"src":1687},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206134916260-300x163.png",[11,1689,1690],{},"上图左右采用的是同样的光照环境，虽然右图的光照是物理正确的，但是并不是所想要达到的风格。",[11,1692,1693],{},"对Shader进行修改，在ShadowMap上添加一个Bias来对光照进行修正。由于是直接在被投影表面进行的调整，导致有的时候会将想要保留的阴影也抹除的情况，例如头发等在脸部的投影也会消失。",[11,1695,1696],{},"本作由于没有采用将头发的阴影另外绘制的方法，所以并没有完全解决这个问题，只是通过调整参数来尽可能的达到一个好的效果。",[29,1698,1699],{"id":1699},"角色风格统一",[11,1701,1702],{},"本作由于角色设计由多人进行，所以角色之间会具有不同的风格。",[11,1704,1705],{},"为了让这些风格不同的角色在游戏中很好的进行风格融合，主要对头身比和瞳孔的表现进行了统一。",[11,1707,1708],{},"最开始的时候虽然为了忠实于角色设计，没有统一头身比，但是实际运行中发现，头身比不同的角色在同一个场景中的话，头更大的角色看起来就像是穿着人偶服一样，效果很不好，所以最后还是做了统一。",[11,1710,1711],{},"瞳孔方面，不同的角色原案会有不同的风格。这个也实际尝试过之后发现违和感很强，进行了统一性的调整。",[11,1713,1714],{},[44,1715],{"alt":46,"src":1716},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206155637508-300x132.png",[11,1718,1719],{},"表情全部采用的是骨骼动画，基本会按照原画的风格来。然后在其基础上制作更多的派生表情。表情制作时如果出现无法完全判断正面表情的情况，会使用正面的原画作为基准进行想象来进行制作。",[11,1721,1722],{},"此外，以基础表情为基础，在场景中还可以将表情进行融合形成更多的表情。将眉眼左右和嘴型各自设置成不同的表情并保存成Preset来使用的话，就可以组合出新的表情。",[11,1724,1725],{},"对于每个角色，还会在「あ」的口型之上有一个用于大叫的表情。",[11,1727,1728],{},"除了骨骼的动画表情之外，还有用于叠加的四个特殊表情：",[11,1730,1731],{},[44,1732],{"alt":46,"src":1733},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206161506339-300x98.png",[11,1735,1736],{},"脸红的表情是直接通过透明度来调节的。",[11,1738,1739],{},"眼泪的话，是通过在眼泪的模型的Shader中进行顶点动画来实现的。而流下来的眼泪则是从眼泪的模型为起点播放UV动画来实现的。",[29,1741,1742],{"id":1742},"可动饰物",[11,1744,1745],{},"本作的可动饰物是通过使用Bullet物理引擎来实现的。",[11,1747,1748],{},"专门制作了一个场景，方便在其中通过编辑器对可动饰物进行调整和设置。可以对各个骨骼进行角度范围、质量、惯性、摩擦系数、反弹系数、速度衰减、角度衰减等参数进行逐个调整。另外也可以添加碰撞来防止可动饰物穿模。",[11,1750,1751],{},[44,1752],{"alt":46,"src":1753},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206164422793-300x221.png",[11,1755,1756],{},"对于一些单纯依靠物理无法很好的实现的部分，会将其根骨骼挂到动画中，由动画进行控制。",[11,1758,1759],{},"除了骨骼的纵向运动传递外，也会设定横向的运动传递。让头发、振袖和裙子可以同步的进行摇动。",[11,1761,1762],{},"制作过程中，会在编辑器中，对行走、跑步、左右转弯、坐下等动作进行预览，以此为基准保证不会穿模。在实际的场景中，如果出现穿模的话，也会进行微调。",[29,1764,581],{"id":581},[11,1766,1767],{},"将动画风和写实风进行融合过程中，这两者最大的不同点在于：夸张和省略。",[11,1769,1770],{},"根据角色的部位不同，会采用不同的写实与动画的比例。",[11,1772,1773],{},[44,1774],{"alt":46,"src":1775},"\u002Fwp-content\u002Fuploads\u002F2021\u002F02\u002Fimage-20210206163252315-1-300x144.png",[11,1777,1778],{},"角色的头部、脸和头发，会想要更多的保留原画中的印象。",[11,1780,1781],{},"衣服的质感、整体的阴影，则会更多的采用写实的方案。",[11,1783,1784],{},"本次的结论是：只要角色的脸的印象很好的保留的话，其他的部分采用写实的风格也不会有多少违和感。",[11,1786,1787],{},"以下是官方总结的想要改进的部分：",[211,1789,1790,1793,1796,1799,1802,1805],{},[214,1791,1792],{},"希望能对阴影的形状进行控制",[214,1794,1795],{},"更加强化脸部的细节",[214,1797,1798],{},"添加更多的表情。追加母音区分大小的表情以及固有表情。",[214,1800,1801],{},"强化眼睛的表现力。即便画面中只有眼睛也能有足够的细节。",[214,1803,1804],{},"头发的强化。根据光照和摄像角度有更多的变化。",[214,1806,1807],{},"手的表演的强调。改善手的现状和添加细节。",[11,1809,1810],{},"由于是官方自己的想要改进的部分，所以是否会改进以及是否之后会有更多的需求就不确定了~",{"title":46,"searchDepth":327,"depth":593,"links":1812},[1813,1814,1822,1832,1836,1837,1838],{"id":1232,"depth":327,"text":1232},{"id":1277,"depth":327,"text":1277,"children":1815},[1816,1817,1818,1819,1820,1821],{"id":1303,"depth":593,"text":1303},{"id":1324,"depth":593,"text":1324},{"id":1336,"depth":593,"text":1336},{"id":1358,"depth":593,"text":1358},{"id":1404,"depth":593,"text":1405},{"id":1417,"depth":593,"text":1418},{"id":1427,"depth":327,"text":1427,"children":1823},[1824,1825,1826,1827,1828,1829,1830,1831],{"id":1430,"depth":593,"text":1430},{"id":1476,"depth":593,"text":1476},{"id":1503,"depth":593,"text":1503},{"id":1525,"depth":593,"text":1526},{"id":1561,"depth":593,"text":1561},{"id":1589,"depth":593,"text":1589},{"id":1603,"depth":593,"text":1603},{"id":1620,"depth":593,"text":1620},{"id":1640,"depth":327,"text":1640,"children":1833},[1834,1835],{"id":1665,"depth":593,"text":1665},{"id":1679,"depth":593,"text":1679},{"id":1699,"depth":327,"text":1699},{"id":1742,"depth":327,"text":1742},{"id":581,"depth":327,"text":581},"2021-02-08",{"layout":621,"status":622,"published":623,"author":1841,"author_login":809,"author_email":810,"author_url":811,"wordpress_id":1842,"wordpress_url":1843,"date_gmt":1844,"excerpt":1845},{"display_name":809,"login":809,"email":810,"url":811},3467,"\u002F?p=3467","2021-02-08 14:11:56 +0000",{"type":8,"value":1846},[1847],[11,1848,1216],{},"\u002F2021-02-08-cedec2020-new-sakura-wars-note",{"title":1211,"description":1216},"_legacy\u002F2021\u002F2021-02-08-cedec2020-new-sakura-wars-note",[636],"3p4fEy7QwflLwkHzYwLAcwoicxmOb9p8krSJijeEP6A",222,1788763176487]