3dsMaxではシンメトリモディファイヤがあるので、対称移動がない。 それを可能にしようというスクリプトです。vertexMirror.ms
以下スクリプト macroScript SymVertex category:"Script" ( --/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ -- 使い方 --MaxScript→スクリプトを起動→このスクリプトを起動 --カスタマイズ→ユーザーインターフェースをカスタマイズ→ ツールバーのカテゴリにScriptが追加されているのでツールバーにドラッグ --ボタンを押して起動 -- --起動時、オブジェクト選択変更時、軸切り替え時にシンメトリマップを作成する。(頂点数が多くなると重い) --SYMMETRYのボタンがONになっている間は常に対称化を行う --X,Y,Zそれぞれのチェックボックスで対称軸を切り替える --thresholdで頂点のずれを補完できる(完全シンメトリなら標準のままが安全 --/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ --Globals global SymVertex--ロールアウト作成用変数 rollout SymVert "SymVertex"--シンメトリマップ作成ロールアウト ( checkbox axisX "X" checked:true align:#left across:3 checkbox axisY "Y" checked:false align:#center checkbox axisZ "Z" checked:false align:#right spinner thres "threshold:" range:[0,100,0.01] align:#center--スレッショルド値 checkbutton symmetryVert "SYMMETRY" checked:true timer clockVert "clockVert" interval:100--時計最後の数で反映の早さが変わる local mapFlag = 0 local vcount = 0 local objHandle = 0 local mouseF = 1--マウスフラグ local symFlag--シンメトリマップがあるかどうか判定する変数 local symAry = #(#(),#())--シンメトリマップ用二次元配列 local symAxis--軸変換切り替え用変数 fn getLocalVert obj n = (--ローカル頂点位置取得 if classof obj.baseobject == Editable_Poly then return(coordsys local polyOp.getVert obj n)--ローカル頂点座標取得 else if classof obj.baseobject == Editable_Mesh do return(coordsys local meshop.getVert obj n)--編集可能メッシュ ) fn createSymmetry obj = (--シンメトリマップ作成関数 if selection.count == 1 do--単一選択かどうか ( if classof obj.baseobject == Editable_Poly then vcount = obj.verts.count--頂点数 else if classof obj.baseobject == Editable_Mesh do vcount = obj.numverts --編集可能メッシュ if axisX.checked then symAxis = [-1,1,1]--軸反転用point3値、チェックの入っている軸に対応した値を代入 else if axisY.checked then symAxis = [1,-1,1] else if axisZ.checked do symAxis = [1,1,-1] for i=1 to vcount do--頂点の数だけ繰り返す ( local verIda = getLocalVert obj i if axisX.checked then verId = verIda.x--point3値のままでは比較できないので、抜き出し else if axisY.checked then verId = verIda.y else if axisZ.checked do verId = verIda.z if ((verId - thres.value) < 0 and (verId + thres.value) > 0 ) then--頂点がセンターなら配列Aと配列Bに同じ値を代入 ( symAry[1][i] = i symAry[2][i] = i ) else ( verIda = verIda * symAxis--頂点位置の対称性比較のため、頂点座標マイナス化 symAry[2][i] = -1--非対称頂点判別フラグforが終わったあとでも-1なら同じ頂点インデックスを代入する for j=1 to vcount do ( local verIdb = getLocalVert obj j if ((verIda.x - thres.value) < verIdb.x and (verIda.x + thres.value) > verIdb.x ) and ((verIda.y - thres.value) < verIdb.y and (verIda.y + thres.value) > verIdb.y ) and ((verIda.z - thres.value) < verIdb.z and (verIda.z + thres.value) > verIdb.z ) do--二次元配列の一項目に正、二項目に負、の値を代入 ( symAry[1][i] = i symAry[2][i] = j ) ) if symAry[2][i] == -1 do--真ん中でもなく対称位置に何もない、つまり非対称頂点を判別 ( symAry[1][i] = i symAry[2][i] = i ) ) ) symFlag = 1--シンメトリマップ存在フラグ、これが立っていないと対称化が動かない print symAry--確認用に配列書き出し print vcount--同じく頂点数書き出し print "Created Symmetry" ) ) fn vertWget obj = (--ソフト選択の重みが乗っている頂点を判別し配列に突っ込む関数 local wcount = 1 local vertW global verInd = #() for i = 1 to vcount do ( if classof obj.baseobject == Editable_Poly then vertW = polyOp.getVDataValue obj 1 i --頂点の重み else if classof obj.baseobject == Editable_Mesh do vertW = meshop.getVDataValue obj 1 i--編集可能メッシュ if vertW != 0 do ( verInd[wcount] = i wcount += 1 ) ) ) on clockVert tick do--対称化処理 ( if selection.count == 1 do--オブジェクトが選択されているか ( local baseObj = 0--ベースオブジェクト判定変数 if classof $.baseobject == Editable_Poly then baseObj = 1--編集可能ポリゴンなら1 else if classof $.baseobject == Editable_Mesh do baseObj = -1--編集可能メッシュなら-1 if baseObj == 1 then sVert = polyOp.getVertSelection $ as array --選択中の頂点を取得し、bitarray値から配列に変換する else if baseObj == -1 do sVert = getVertSelection $ as array--編集可能メッシュ if sVert != undefined do--頂点選択しているなら実行 ( if symAry[1][1] == undefiend do mapFlag = 0--配列が空ならシンメトリマップ作成 if mapFlag == 0 do--起動時にシンメトリマップ作成 ( createSymmetry $ mapFlag = 1--マップが出来たよ objHandle = $.handle--選択しているオブジェクトのIDを代入する ) if objHandle != $.handle do--もし、違うオブジェクトが選択されたらシンメトリマップ作成 ( createSymmetry $ objHandle = $.handle ) if symmetryVert.checked and symFlag == 1 do--シンメトリボタンONで対称編集可能なら実行 ( local mouseP = mouse.mode--マウスの状態を見る変数 if mouseP == 0 and mouseF == 0 do--マウスがクリックされていない状態でフラグが立っていない場合 ( if baseObj == 1 and $.useSoftSel == false then verInd = polyOp.getVertSelection $ as array --選択中の頂点を取得し、bitarray値から配列に変換する。 else if baseObj == -1 and meshop.getSoftSel $ == false do verInd = getVertSelection $ as array--編集可能メッシュ for i = 1 to verInd.count do ( local verPos = getLocalVert $ symAry[1][verInd[i]]--ローカル頂点位置取得 if symAry[1][verInd[i]] != symAry[2][verInd[i]] do--真ん中、もしくは非対称でなければ実行 ( if baseObj == 1 then (coordsys local polyOp.setVert $ symAry[2][verInd[i]] (verPos * symAxis))--対称化 else if baseObj == -1 do (coordsys local meshop.setVert $ symAry[2][verInd[i]] (verPos * symAxis))--編集可能メッシュ ) ) mouseF = 1 redrawViews()--結果反映 ) if mouseP == 2 and mouseF == 1 do--対称化を移動後に一度だけ動かすためのフラグ処理(タイマーでループしているので、、、 ( if baseObj == 1 and $.useSoftSel then vertWget $--最初の位置の影響値を取るためにここでウェイトを配列化(ソフト選択の影響値は動かす度に変わるので else if baseObj == -1 and meshop.getSoftSel $ do vertWget $ mouseF = 0 ) ) ) ) ) on axisX changed theState do--チェックボックスがオンになるとほかのがオフになる&シンメトリマップ作成 ( if theState do ( axisY.triState = 0; axisZ.triState = 0; createSymmetry $) ) on axisY changed theState do ( if theState do ( axisX.triState = 0; axisZ.triState = 0; createSymmetry $) ) on axisZ changed theState do ( if theState do ( axisX.triState = 0; axisY.triState = 0; createSymmetry $) ) ) try ( closeRolloutFloater SymVertex )catch () SymVertex = newRolloutFloater "symVert" 180 100 addRollout SymVert SymVertex )
現状の自分の使っているカメラスクリプト ターゲットとペアレントを設定して起動 var target : Transform; var parent : Transform; var zoomSpeed = 0.5; var panSpeed = 1.0; var backSpeed = 5.0; var distance = 3.0; var distanceMin = 0.5; var distanceMax = 20.0; var hight = 0.0; var width = 0.0; var orbit_xSpeed = 17.0; var orbit_ySpeed = 17.0; var orbit_yMin = -90; var orbit_yMax = 90; private var firstTargetPos = Vector3(0.0, 0.0, 0.0); private var firstParentPos = Vector3(0.0, 0.0, 0.0); private var angles = Vector3(0.0, 0.0, 0.0); private var firstCameraPos = Vector3(0.0, 0.0, 0.0); var firstRot = Quaternion(0.0, 0.0, 0.0,0.0); private var autoRotate = false; private var oldTime = 0.0; private var x = 0.0; private var y = 0.0; private var resetPos = Vector3(0.0,0.0,0.0); @script AddComponentMenu("Camera-Control/VRMouse Orbit") function Start () { target.transform.position = firstTargetPos + parent.transform.position; transform.position = firstCameraPos; transform.rotation = firstRot; angles = transform.eulerAngles; x = angles.y; y = angles.x; // Make the rigid body not change rotation if (GetComponent.()) GetComponent. ().freezeRotation = true; resetPos = target.transform.position; } function LateUpdate (){ //keyEvent if (Input.GetKey("p")){ print("hight_" + hight + "width_" + width); print(" Rot_X" + transform.rotation[0] + " Y_" + transform.rotation[1] + " Z_" + transform.rotation[2] + " W_" + transform.rotation[3]); } //zoom if (Input.GetMouseButton(1)) { distance += Input.GetAxis("Mouse Y") * zoomSpeed; distance = Mathf.Clamp (distance, distanceMin, distanceMax); } //pan target.transform.rotation = transform.rotation; if( Input.GetMouseButton(2)){ var panVal = panSpeed * (distance / 20); if(Input.GetAxis("Mouse Y") != 0){ hight += Input.GetAxis("Mouse Y") * panVal; } if(Input.GetAxis("Mouse X") != 0){ width += Input.GetAxis("Mouse X") * panVal; } } target.transform.position -= (target.transform.position - parent.transform.position) * Time.deltaTime * backSpeed; //orbit if (target && Input.GetMouseButton(0) && !Input.GetKey("left alt")) { x += Input.GetAxis("Mouse X") * orbit_xSpeed; y -= Input.GetAxis("Mouse Y") * orbit_ySpeed; } y = ClampAngle(y, orbit_yMin, orbit_yMax); var rotation = Quaternion.Euler(y, x, 0); var position = rotation * Vector3(-width, -hight, -distance) + target.position; transform.rotation = rotation; transform.position = position; } static function ClampAngle (angle : float, min : float, max : float) { if (angle < -360) angle += 360; if (angle > 360) angle -= 360; return Mathf.Clamp (angle, min, max); }
現状の自分が使っているシェーダー ポイントは二次元テクスチャを使ってリムっぽく影を出しているところ 法界3rdを参考にしました。 Shader "ukoku_toon" { Properties { _Color ("Main Color", Color) = (1,1,1,1) _ShadowColor("Shadow Color", Color) = (0,0,0,1) _MainTex ("Diffuse(RGB)", 2D) = "white" {} _SpecTex("Specular(RGBA)", 2D) = "black" {} _BumpMap("Bump(RGB)", 2D) = "bump" {} _ShadowTex("Shadow(RGBA)", 2D) = "black" {} _CelTex ("Cel(RGB)", 2D) = "white" {} _RimTex("RimTex", 2D) = "black"{} _ShadowBias("ShadowBias", range(-0.5, 0.5)) = 0.0 _ShadowAmount("ShadowAmount", range(0.01, 1.0)) = 0.0 _RimColor("Rim Color", Color) = (0,0,0,1) _Front("Front", range(0.0, 1)) = 0.0 _Depth("Depth", range(0.0, 1.0)) = 0.0 _OutlineColor("Outline Color", Color) = (0,0,0,1) _OutlineWidth("Outline Width", float) = 0.05 } SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry" "LightMode"="ForwardBase" } Pass{ CGPROGRAM #pragma target 3.0 #pragma multi_compile_fwdbase #pragma vertex vert #pragma fragment frag #include "AutoLight.cginc" #include "UnityCG.cginc" half4 _Color; half4 _ShadowColor; sampler2D _MainTex; sampler2D _CelTex; sampler2D _RimTex; sampler2D _BumpMap; sampler2D _SpecTex; sampler2D _ShadowTex; half4 _RimColor; half _Depth; half _ShadowBias; half _ShadowAmount; half _Shininess; float _Front; float4 _MainTex_ST; fixed4 _LightColor0; float3 _MyVector; struct v2f{ float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float3 lightDir : TEXCOORD1; float3 viewDir : TEXCOORD2; float3 normal : TEXCOORD3; LIGHTING_COORDS (4,5) float4 color : COLOR; }; v2f vert(appdata_full v){ v2f o; //v.color = _Color; o.color = v.color; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); float3 binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w; float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal); o.lightDir = normalize(ObjSpaceLightDir(v.vertex)); o.lightDir = mul(rotation, o.lightDir); o.viewDir = normalize(ObjSpaceViewDir(v.vertex)); o.viewDir = mul(rotation, o.viewDir); //o.lightDir = normalize(ObjSpaceLightDir(v.vertex)); //o.viewDir = normalize(ObjSpaceViewDir(v.vertex)); o.normal = v.normal; float4 vdir = mul(UNITY_MATRIX_MV, float4(v.normal, 0)); float2 offset = TransformViewToProjection(vdir.xy); //pushPoly if (v.color.g == 1.0) { o.pos.z += (_Depth / 100 / o.pos.w); } if (v.color.g == 0.0) { o.pos.z -= (_Depth / o.pos.w); } TRANSFER_VERTEX_TO_FRAGMENT( o ); return o; } half4 frag (v2f i) : COLOR { //comment : NormalMap float3 norm = UnpackNormal(tex2D(_BumpMap, i.uv)); norm = normalize(norm); //comment : diffuse float4 texcol = tex2D (_MainTex,i.uv); //comment : Light0 float4 spectex = tex2D(_SpecTex, i.uv); float3 lightColor = _LightColor0 * spectex.rgb; float specPow = (spectex.r + spectex.g + spectex.b) / 3; //comment : Speculer float3 hv = normalize(i.viewDir + (i.lightDir * texcol.a)); float lspec = dot(hv, norm); float p = pow(lspec, (1 - specPow)); float4 specWeight = smoothstep(1 - specPow, 1 - specPow + _ShadowAmount, p); //comment : harf lambert + vertexColor float2 toon = dot(normalize((i.viewDir * _Front) + (i.lightDir * (1 -_Front))),norm) - _ShadowBias; toon = clamp(toon,0,1); //comment : shadow float4 shadowcol = LIGHT_ATTENUATION( i ); float4 shadowtex = tex2D(_ShadowTex, i.uv); float4 celcol = max((tex2D(_CelTex, half2(toon.x, saturate(dot(i.viewDir, norm)))) * min(shadowcol + _ShadowColor,1)),0); float3 cel = min(celcol.rgb + shadowtex.rgb, 1); //comment : rim float rim = saturate(dot(i.viewDir,norm)); float3 rimcol = tex2D (_RimTex, half2(rim,0)).rgb * _RimColor.rgb; //comment : color out float4 outColor; outColor.rgb = _Color * texcol.rgb * celcol.rgb * (_LightColor0.rgb); outColor.rgb += (lightColor * clamp(specWeight.rgb,0,1) * smoothstep(0.1, 0.9, rim)) + rimcol.rgb; outColor.a = 1.0; return outColor; } ENDCG } Pass{ Cull Front Lighting Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" float4 _OutlineColor; float _OutlineWidth; sampler2D _MainTex; float4 _MainTex_ST; struct v2f { float4 pos : SV_POSITION; fixed2 uv : TEXCOORD0; float3 normal : TEXCOORD1; }; v2f vert(appdata_full v) { v2f o; //v.color = _Color; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); float3 norm = mul(UNITY_MATRIX_MV,float4(v.normal,0)); float2 offset = TransformViewToProjection(norm.xyz); o.pos.xy += (_OutlineWidth / 100 * offset) * v.color.a; o.pos.z += 0.000001 / o.pos.w; return o; } half4 frag(v2f i) : COLOR{ //comment : diffuse float4 texcol = tex2D(_MainTex,i.uv); return half4(texcol * _OutlineColor.rgb,1); } ENDCG } } FallBack "Diffuse" }
Mayaのジョイント(骨)にはジョイントの方向という独特の概念がある。 ジョイントの回転とは別に方向を設定することで、バインド時のポーズに戻したい時に、 回転値を0にすることで、元に戻すことができるらしい。 じゃあなぜバインドポーズがあるのか、と言ってはいけない。 とにかく、このジョイントの方向がいろいろややこしいので、メモがてらにまとめてみたいと思う。 Maya道にもこの辺りは載っていたと思うので読んでみると良いと思う。 ・前提 Mayaでは回転値をジョイントの方向に移して、0にするのが基本的なセットアップの準備だ。 これに関しては、ジョイント位置を設定した後に、トランスフォームのフリーズをすれば良い。 それとは別に、仕様によってはジョイントの方向を0にして、回転値に値を残したままにすることもある。 トランスフォームの値を0にしたい時に、回転値を0にする場合がフリーズだったからといって、 トランスフォームのフリーズを使ってはいけない。 回転値が0のまま、ジョイントの方向も0になる。こうしてしまうと戻すのが面倒になる。 回転値の場合、ジョイントの方向の場合、以下にそれぞれ記す。 ・回転を0にしたい時 ジョイントを回転と一軸移動で位置を設定したのち、修正>トランスフォームのフリーズを使用して、 回転をジョイントの方向へ移す。 回転のみにチェックを入れると良い。
↓
・ジョイントの方向を0にしたい時 ジョイントの方向を回転に戻したい場合は、フリーズでは戻らないので、 以下のような簡単なスクリプトが必要になる。 string $Obj[] = `ls -sl -flatten`; for($i in $Obj){ $rx = getAttr ($i + ".jointOrientX"); $ry = getAttr ($i + ".jointOrientY"); $rz = getAttr ($i + ".jointOrientZ"); setAttr ($i + ".jointOrientX") 0; setAttr ($i + ".jointOrientY") 0; setAttr ($i + ".jointOrientZ") 0; rotate -r -os ($rx) ($ry) ($rz) $Obj; } ・スキニングされているモデルでボーンに対して色々したい時の、ウェイトの逃がし方 以下のスクリプトを使用して、ウェイトを逃がすと良い。 頂点が変わらなければそのまま行けるので、一時的な退避ではめっぽう使いやすい。 技研のウェイトスクリプト ワークフローとしては、 ウェイトを逃がす ↓ ジョイント位置設定 ↓ バインドポーズ削除 ↓ スキンバインド ↓ ウェイト読み込み といった感じ。
なぜなのかは知らないが、新規作成したポリゴンはIDが1で固定されている。 設定できればいいのに、できないのでもどかしい。 というわけでスクリプトを作成しました。 実行するとポリゴンIDとスムージンググループの値を設定するスピナーが出てくる。 それを好きな値に設定して、STARTを押せばOK! ポリゴン編集オブジェクトを選択していて、かつエッジ選択の状態の時に動作する。 ポリゴンが増えると自動でIDとスムージンググループを設定するぞ! エッジカットとかでも設定するのでそこらへんは注意ね。 ちなみに編集可能メッシュ及び、編集可能ポリゴンモディファイヤには非対応。 スクリプトを一回実行するとカテゴリのScriptが作られるので、 ユーザーインターフェースをカスタマイズから適当にボタン化とかさせましょう。 以下リンクスクリプト
以下スクリプト macroScript IDSet category:"Script" ( -- Globals global IDSetDialog rollout IDSet "IDSet"--ID変更 ( spinner ID "ID:" range:[1,20,1] type:#integer align:#left across:2--ID spinner GP "GP:" range:[1,32,1] type:#integer align:#left across:2--スムージンググループ checkbutton idSet "START" checked:false align:#right timer clockVert "clockVert" interval:100--時計最後の数で反映の早さが変わる local mouseF = 1--マウスフラグ local polyAry = #()--ID変更用二次元配列 local polyold = 0 on clockVert tick do--ID変更処理 ( if selection.count == 1 and idSet.checked do--オブジェクトが選択されているか ( if classof $.baseobject == Editable_Poly then sEdge = polyOp.getEdgeSelection $ as array --選択中のエッジを取得し、bitarray値から配列に変換する else break if sEdge != undefined do--エッジ選択しているなら実行 ( local mouseP = mouse.mode--マウスの状態を見る変数 if mouseP == 0 and mouseF == 0 do--マウスがクリックされていない状態でフラグが立っていない場合 ( polyCnt = polyOp.getNumFaces $ if polyCnt != polyold then ( polyadd = polyCnt - polyold for i = 1 to polyadd do ( polyAry[i] = (polyCnt + 1 - i); ) undo off ( polyop.setFaceMatID $ polyAry ID.value polyop.setFaceSmoothGroup $ polyAry (2 ^ (GP.value - 1)) add:false ) ) mouseF = 1 redrawViews()--結果反映 ) if mouseP == 0 do--マウスがクリックされていない状態 ( polyold = polyOp.getNumFaces $ ) if mouseP == 2 and mouseF == 1 do--対称化を移動後に一度だけ動かすためのフラグ処理(タイマーでループしているので、、、 ( mouseF = 0 ) ) ) ) ) try ( closeRolloutFloater IDSetDialog )catch () IDSetDialog = newRolloutFloater "IDSet" 200 90 addRollout IDSet IDSetDialog )
3dsMaxで選択したポリゴンを対称複製するスクリプト 何気に標準機能でないので作成 シンメトリとかミラーとかいいから編集ポリゴン内で対称化させろよという人向け 最終結果を表示しないで使うべし 別に連結とかはしないので、使用後に連結0.01とかすべし。 これ使うとシンメトリ使わないで良いのでウェイト壊すことなくモデリングできるぞ! 一回実行するとスクリプトカテゴリが作られるので、 ユーザーインターフェースをカスタマイズから適当にボタン化とかさせましょう。 以下リンクスクリプト
以下スクリプト macroScript symPoly category:"Script" ( $.EditablePoly.detachToElement #Face keepOriginal:on local selFace = polyOp.getFaceSelection $ local verId = polyop.getVertsUsingFace $ selFace as array local vercount = verId.count local vArray = #() for i = 1 to vercount do ( vArray[i] = [(polyOp.getVert $ verId[i]).x,0,0] vArray[i].x = (vArray[i].x * -1) * 2 ) $.EditablePoly.flipNormals 1 polyop.moveVert $ verId vArray node:$ )
使う人いるのかわからないけど、とりあえず置いておく系 アウトラインアリ、影アリでモデリングしたい人向け。 ハーフランバートなのでランバートにしたい人は float cel = dot(Normal,LightDir) * 0.5 + 0.5; を float cel = dot(Normal,LightDir); こうするとおk テクスチャ更新すると落ちるかも。 アウトラインの太さはシェーダー直にいじって調整するとよろし 以下シェーダー //toon.fx// //ライト// float3 lightDir : Direction < string UIName = "Light Direction"; string Object = "TargetLight"; int RefID = 0; > = {0.0, 0.0, 0.0}; //影色// float4 Shadowcol < string UIName = "ShadowColor"; > = float4( 0.0f, 0.0f, 0.0f, 1.0f ); //ラインカラー// float4 Linecol < string UIName = "LineColor"; > = float4( 0.0f, 0.0f, 0.0f, 1.0f ); //テクスチャ// texture Tex0 : DiffuseMap < string name = ""; string UIName = "Base Texture"; int Texcoord = 0; >; //トゥーンテクスチャ// texture Tex1 : CelMap < string name = ""; string UIName = "Cel Texture"; int Texcoord = 1; >; //座標行列// float4x4 Matrix_MVP : WORLDVIEWPROJ; float4x4 Matrix_MV : WORLDVIEW; float4x4 World : WORLD; float4x4 View : VIEW; float4x4 Matrix_P : PROJECTION; //頂点シェーダー// struct VS_OUTPUT { float4 Pos : POSITION; float4 Diff : COLOR0; float2 Tex : TEXCOORD0; float2 Normal : TEXCOORD1; float2 LightDir : TEXCOORD2; float2 ViewDir : TEXCOORD3; }; VS_OUTPUT VS( float3 Pos : POSITION, float3 Norm : NORMAL, float2 Tex : TEXCOORD0, float2 Normal : TEXCOORD1, float2 LightDir : TEXCOORD2, float2 ViewDir : TEXCOORD3 ) { VS_OUTPUT o = (VS_OUTPUT)0; o.LightDir = normalize(lightDir); //ライト方向// float3 P = mul(float4(Pos, 1),(float4x4)World); //頂点位置// o.Normal = normalize(mul(Norm,(float3x3)World)); //法線// o.ViewDir = normalize(P); //視線方向// o.Pos = mul(float4(Pos,1),Matrix_MVP); //頂点位置// o.Tex = Tex; return o; } //UV設定// sampler Sampler = sampler_state { Texture = (Tex0); MipFilter = LINEAR; MinFilter = LINEAR; MagFilter = LINEAR; ADDRESSU = WRAP; ADDRESSV = WRAP; }; sampler Sampler1 = sampler_state { Texture = (Tex1); MipFilter = LINEAR; MinFilter = LINEAR; MagFilter = LINEAR; ADDRESSU = CLAMP; ADDRESSV = CLAMP; }; //フラグメントシェーダー// float4 PS( float4 Diff : COLOR0, float4 Spec : COLOR1, float3 Tex : TEXCOORD0, float2 Normal : TEXCOORD1, float2 LightDir : TEXCOORD2, float2 ViewDir : TEXCOORD3 ) : COLOR { //トゥーン// float cel = dot(Normal,LightDir) * 0.5 + 0.5; float4 celTex = tex2D(Sampler1, cel); celTex = min(celTex + Shadowcol, 1); //テクスチャ// float4 color = tex2D(Sampler, Tex); color = color * celTex; return color ; } //アウトライン// struct v2f_Line { float4 Diff : COLOR0; float4 pos : POSITION; }; v2f_Line vert( float3 pos : POSITION, float3 norm : NORMAL ) { v2f_Line o = (v2f_Line)0; o.pos = mul(float4(pos,1),Matrix_MVP); float3 normal = mul (float4(norm,0),Matrix_MV); normal.x *= Matrix_P[0][0]; normal.y *= Matrix_P[1][1]; o.Diff = Linecol; //ライン太さ// o.pos.xy += 0.05 * normal.xy * lerp(o.pos.w,1,0.99); return o; } //フラグメントシェーダー// float4 frag_Line( float4 Diff : COLOR0 ) : COLOR { float4 color = Diff; return color ; } //テクニック// technique DefaultTechnique { pass P0 { // shaders CullMode = None; VertexShader = compile vs_2_0 VS(); PixelShader = compile ps_2_0 PS(); } pass P1 { // shaders CullMode = 3; VertexShader = compile vs_2_0 vert(); PixelShader = compile ps_2_0 frag_Line(); } } //ライン無し// technique NoLine { pass P0 { // shaders CullMode = None; VertexShader = compile vs_2_0 VS(); PixelShader = compile ps_2_0 PS(); } }
最近シェーダーの勉強にお熱なので自分用まとめ。いろいろあやふや シェーダーはランバートシェーダーが基本らしい。 ランバートの概念は以下のようなもの。 面とライトの角度によって面の明るさが変わる。 なぜなら、ライトに対して面の角度が大きくなるほど、一定の光に対する面積が増えるから。 以下、適当な図参照
そこで、ライトと面の角度がどれくらい違うかわかれば良いのではということで、 内積というものを使う。 シェーダー内では float lambert= dot(i.normal,i.lightDir); と書いている所。 内積とは正規化されたベクトルを入れてやれば、角度が帰ってくるものらしい。 詳しくは知らないけど、とりあえずそう覚えたら問題なさそう。 角度の違いによって、-1〜0〜1の値が入るので、それを色に乗算してやればランバートになる。 以下シェーダー Shader "Custom/myLambert" { Properties { _Color ("Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } Pass{ CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" float4 _Color; struct v2f{ float4 pos : SV_POSITION; float3 lightDir : TEXCOORD0; float3 normal : TEXCOORD1; float4 color : COLOR; }; v2f vert(appdata_base v){ v2f o; //頂点位置 o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //法線 o.normal = normalize(v.normal); //ライトの向き o.lightDir = normalize(ObjSpaceLightDir(v.vertex)); return o; } float4 frag (v2f i) : COLOR { float4 o; //ランバートの計算 float lambert= dot(i.normal,i.lightDir); //基本色に乗算 o.rgb = _Color.rgb * lambert; o.a = 1; return o; } ENDCG } } FallBack "Diffuse" }
ついにレースとかをアルファで抜きたい時がきました。 というわけで、透明化対応させる方法を書きます。 ちゃんとわかってない気がするので、より良い方法があれば教えていただきたく候。 とりあえず、上のヤツに プロパティに _Cutoff ("Alpha cutoff", Range (0,0.9)) = 0.5 を追加。 #pragmaの行に alphatest:_Cutoffを追加 Fallbackを "Transparent/Cutout/VertexLit" に変更 Tagsは変えたほうがいいのかどうかいまいちわからない感じ。 一応{"Queue"="AlphaTest" "RenderType"="TransparentCutout" }に変えてます。 アウトラインのパスもuvでアルファ抜けるように適宜いじってやります。 で変えた結果が以下。
明らかに影がおかしいですね。 エロイ で、色々試行錯誤して見た結果。 プロパティに_Color ("Main Color", Color) = (1, 1, 1, 1) を追加したら解決しました。 それが以下
ちゃんと出てますね。 _Color ("Main Color", Color) = (1, 1, 1, 1) は計算にまったく使ってないので色々腑に落ちないですが、 とりあえず出たのでコレでいいかなー _Colorなくてもできる方法知ってる人は教えてください。 以下ソース、CelTexは下の画像みたいなのを使うといいです
Shader "Custom/transTest" { Properties{ _Color ("Main Color", Color) = (1, 1, 1, 1) _MainTex("MainTex", 2D) = "white"{} _CelTex("CelTex", 2D) = "black"{} _Gross("Gross", Range (0.03, 1)) = 0.1 _SpColor("SpecColor", Color) = (0,0,0,1) _OutlineColor("Outline Color", Color) = (0,0,0,1) _OutlineWidth("Outline Width", Range(0,0.02)) = 0.01 _OutlineDepth("Outline Depth", Range(0,1)) = 0.5 _ShadowColor("ShadowColor", Color) = (0,0,0,1) _Cutoff ("Alpha cutoff", Range (0,0.9)) = 0.5 } SubShader { Tags {"Queue"="AlphaTest" "RenderType"="TransparentCutout" } CGPROGRAM #pragma surface surf Cel alphatest:_Cutoff sampler2D _MainTex; sampler2D _CelTex; float4 _ShadowColor; half _Gross; half _SpecPower; float4 _SpColor; struct EditorSurfaceOutput { half3 Albedo; half Alpha; half4 Custom; }; half4 LightingCel (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) { float4 lightColor = _LightColor0 * _SpColor * 2; float nh = max(0, dot(s.Normal, normalize (lightDir + viewDir))); float specWeight = pow(nh, s.Gloss * 128.0) * s.Specular; half NdotL = dot(s.Normal, lightDir); half diff = NdotL * 0.5 + 0.5; half3 celCol = tex2D(_CelTex, float2(diff)); half4 at = (1 - atten) * _ShadowColor; half4 c; c.rgb = ((s.Albedo * _LightColor0.rgb * celCol) + lightColor * specWeight) * (atten + at); c.a = s.Alpha + _LightColor0.a; return c; } struct Input { float2 uv_MainTex; float3 viewDir; }; void surf (Input IN, inout SurfaceOutput o) { half3 color = tex2D(_MainTex, IN.uv_MainTex).rgb; half alpha = tex2D(_MainTex, IN.uv_MainTex).a; o.Gloss = _Gross; o.Specular = _SpColor; o.Albedo = color; o.Alpha = alpha; } ENDCG //Outline Pass { Cull Front Lighting Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" uniform float4 _MainTex_ST; uniform sampler2D _MainTex; uniform fixed _Cutoff; float4 _OutlineColor; float _OutlineWidth; float _OutlineDepth; struct v2f { float4 pos : SV_POSITION; fixed2 uv : TEXCOORD0; }; v2f vert (appdata_base v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); float3 norm = mul ((float3x3)UNITY_MATRIX_MV, v.normal); norm.x *= UNITY_MATRIX_P[0][0]; norm.y *= UNITY_MATRIX_P[1][1]; norm.z *= UNITY_MATRIX_P[2][2]; o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.pos.xyz += _OutlineWidth * norm.xyz * lerp(o.pos.w, 1, _OutlineDepth); return o; } half4 frag(v2f i) : COLOR { half alpha = tex2D(_MainTex, i.uv ).a; clip( alpha - _Cutoff ); return half4(_OutlineColor.rgb,alpha); } ENDCG } } Fallback "Transparent/Cutout/VertexLit" }
なんとなく試行錯誤した結果を書いてみる。 影色を計算している所は以下 //ShadowColor half4 at = (1 - atten) * _ShadowColor; half4 c; c.rgb = ((s.Albedo * _LightColor0.rgb * NdotL)) * (atten*2 + at); attenが影の情報っぽいので反転して影色を乗算したものを後に加算している感じ。 なんか回りくどい気がするのでいろいろ調べたところ、 //ShadowColor half4 at = min((atten + _ShadowColor),1); half4 c; c.rgb = ((s.Albedo * _LightColor0.rgb * NdotL)) * (atten + at); でも同じになるみたいですね。 何か楽な方法があれば教えていただきたい所存。 以下ソースコード Shader "Custom/shadowtest" { Properties{ _MainTex("MainTex", 2D) = "white"{} _ShadowColor("ShadowColor", Color) = (0,0,0,1) } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Cel sampler2D _MainTex; float4 _ShadowColor; struct EditorSurfaceOutput { half3 Albedo; half4 Custom; }; half4 LightingCel (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) { half NdotL = dot(s.Normal, lightDir); //ShadowColor half4 at = (1 - atten) * _ShadowColor; half4 c; c.rgb = ((s.Albedo * _LightColor0.rgb * NdotL)) * (atten*2 + at); c.a = s.Alpha + _LightColor0.a; return c; } struct Input { float2 uv_MainTex; float3 viewDir; }; void surf (Input IN, inout SurfaceOutput o) { half3 color = tex2D(_MainTex, IN.uv_MainTex).rgb; half alpha = tex2D(_MainTex, IN.uv_MainTex).a; o.Albedo = color; o.Alpha = alpha; } ENDCG } Fallback "Diffuse" }