Nao_uの日記 このページをアンテナに追加 RSSフィード

2019-03-10

[][]新居の間取りVR 新居の間取りVR - Nao_uの日記 を含むブックマーク はてなブックマーク - 新居の間取りVR - Nao_uの日記 新居の間取りVR - Nao_uの日記 のブックマークコメント

新居の間取りを図面からモデリングしてVRで確認。壁や家具の配置も簡単に変えられて、トイレやロフトなど図面だけでは広さが実感しにくい所もわかりやすく体感できるので、とても役に立った


スイッチの位置や家事の動線も部屋の中を実際に歩きながら確認できるし、家具や壁紙の色を変えたらどんな雰囲気なるのかもすぐ見れる。部屋の実際の広さは図面の数字だけでは実感しづらいので、こういった用途にVRはほんと最適だと思った


新居の間取りをVRで確認しつつ決めて、今週末の引っ越しのために現地を見に行ったら、ちょうどVRで見たものと同じものがそのまま出来上がっててちょっと不思議な感覚。2Dの図面や平面のCGではわからない部屋のスケール感や光源の具合までちゃんと確認できるので、ほんとVR向きな案件だなぁ、と。


https://twitter.com/Nao_u_/status/1104573498066853888

家のモデリングにはHoudiniを使用。図面に変更があった時にも、大元の枠組みだけ変えれば簡単に修正できるので、プロシージャルモデリングの便利さを実感。


f:id:Nao_u:20190310125218p:image

外構工事の検討もUnityでレイアウトしてVRで確認してた。Unityなら簡単に複数パターンの配置を切り替えながら試せて、HMDを通してみると2DのCGで見るのとけっこう印象が違ってたり、塀の高さを変えたら家の中がどう見えるかの違いなども検討できたりなど、ここでもVRは有用だった

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20190310

2016-07-31

[]気分転換その3 気分転換その3 - Nao_uの日記 を含むブックマーク はてなブックマーク - 気分転換その3 - Nao_uの日記 気分転換その3 - Nao_uの日記 のブックマークコメント

シン・ゴジラ見てきた。

アセットの10式戦車が素敵だったのでやってみたら、思ってた以上に時間がかかってしまった。

ありものの素材を並べて10秒の動画をでっち上げるだけでもこんな手間がかかるんだから、CGの映画を作るのって果てしなく大変な作業なんだなぁ、と

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20160731

2016-07-27

[]気分転換その2 気分転換その2 - Nao_uの日記 を含むブックマーク はてなブックマーク - 気分転換その2 - Nao_uの日記 気分転換その2 - Nao_uの日記 のブックマークコメント

f:id:Nao_u:20160730122230p:image

Wiiバランスボードをつないでホバーボードっぽいものを試してみた。

VRにするとやっぱり酔うし、スノボ風に前後に足を並べると背後にターンするときに倒れそうで危なくて、正面に向くと安定するけど楽しさが減った。

いっそVRなしでやったほうが楽しいかもしれない。Zロールも使えるし。

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20160727

2016-07-17

[]気分転換 気分転換 - Nao_uの日記 を含むブックマーク はてなブックマーク - 気分転換 - Nao_uの日記 気分転換 - Nao_uの日記 のブックマークコメント

f:id:Nao_u:20160717225555p:image

地面に鏡を置いてdofでぼかすとそれっぽい絵が出ることがわかった

これのコースエディタをHoudiniで、とか


あとINSIDEクリアした

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20160717

2016-07-16

[]簡単な鏡の作り方 簡単な鏡の作り方 - Nao_uの日記 を含むブックマーク はてなブックマーク - 簡単な鏡の作り方 - Nao_uの日記 簡単な鏡の作り方 - Nao_uの日記 のブックマークコメント

MirrorReflectionのシェーダーをMirrorReflection.csを入れたオブジェクトに割り当てるだけ。Y+方向に鏡が発生。

Shader "FX/MirrorReflection"

{

Properties

{

_MainTex ("Base (RGB)", 2D) = "white" {}

[HideInInspector] _ReflectionTex ("", 2D) = "white" {}

}

SubShader

{

Tags { "RenderType"="Opaque" }

LOD 100

Pass {

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

#include "UnityCG.cginc"

struct v2f

{

float2 uv : TEXCOORD0;

float4 refl : TEXCOORD1;

float4 pos : SV_POSITION;

};

float4 _MainTex_ST;

v2f vert(float4 pos : POSITION, float2 uv : TEXCOORD0)

{

v2f o;

o.pos = mul (UNITY_MATRIX_MVP, pos);

o.uv = TRANSFORM_TEX(uv, _MainTex);

o.refl = ComputeScreenPos (o.pos);

return o;

}

sampler2D _MainTex;

sampler2D _ReflectionTex;

fixed4 frag(v2f i) : SV_Target

{

fixed4 tex = tex2D(_MainTex, i.uv);

fixed4 refl = tex2Dproj(_ReflectionTex, UNITY_PROJ_COORD(i.refl));

return tex * refl;

}

ENDCG

}

}

}

using UnityEngine;

using System.Collections;

// This is in fact just the Water script from Pro Standard Assets,

// just with refraction stuff removed.

[ExecuteInEditMode] // Make mirror live-update even when not in play mode

public class MirrorReflection : MonoBehaviour

{

public bool m_DisablePixelLights = true;

public int m_TextureSize = 256;

public float m_ClipPlaneOffset = 0.07f;

public LayerMask m_ReflectLayers = -1;

private Hashtable m_ReflectionCameras = new Hashtable(); // Camera -> Camera table

private RenderTexture m_ReflectionTexture = null;

private int m_OldReflectionTextureSize = 0;

private static bool s_InsideRendering = false;

// This is called when it's known that the object will be rendered by some

// camera. We render reflections and do other updates here.

// Because the script executes in edit mode, reflections for the scene view

// camera will just work!

public void OnWillRenderObject()

{

var rend = GetComponent<Renderer>();

if (!enabled || !rend || !rend.sharedMaterial || !rend.enabled)

return;

Camera cam = Camera.current;

if( !cam )

return;

// Safeguard from recursive reflections.

if( s_InsideRendering )

return;

s_InsideRendering = true;

Camera reflectionCamera;

CreateMirrorObjects( cam, out reflectionCamera );

// find out the reflection plane: position and normal in world space

Vector3 pos = transform.position;

Vector3 normal = transform.up;

// Optionally disable pixel lights for reflection

int oldPixelLightCount = QualitySettings.pixelLightCount;

if( m_DisablePixelLights )

QualitySettings.pixelLightCount = 0;

UpdateCameraModes( cam, reflectionCamera );

// Render reflection

// Reflect camera around reflection plane

float d = -Vector3.Dot (normal, pos) - m_ClipPlaneOffset;

Vector4 reflectionPlane = new Vector4 (normal.x, normal.y, normal.z, d);

Matrix4x4 reflection = Matrix4x4.zero;

CalculateReflectionMatrix (ref reflection, reflectionPlane);

Vector3 oldpos = cam.transform.position;

Vector3 newpos = reflection.MultiplyPoint( oldpos );

reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;

// Setup oblique projection matrix so that near plane is our reflection

// plane. This way we clip everything below/above it for free.

Vector4 clipPlane = CameraSpacePlane( reflectionCamera, pos, normal, 1.0f );

//Matrix4x4 projection = cam.projectionMatrix;

Matrix4x4 projection = cam.CalculateObliqueMatrix(clipPlane);

reflectionCamera.projectionMatrix = projection;

reflectionCamera.cullingMask = ~(1<<4) & m_ReflectLayers.value; // never render water layer

reflectionCamera.targetTexture = m_ReflectionTexture;

GL.SetRevertBackfacing (true);

reflectionCamera.transform.position = newpos;

Vector3 euler = cam.transform.eulerAngles;

reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z);

reflectionCamera.Render();

reflectionCamera.transform.position = oldpos;

GL.SetRevertBackfacing (false);

Material[] materials = rend.sharedMaterials;

foreach( Material mat in materials ) {

if( mat.HasProperty("_ReflectionTex") )

mat.SetTexture( "_ReflectionTex", m_ReflectionTexture );

}

// Restore pixel light count

if( m_DisablePixelLights )

QualitySettings.pixelLightCount = oldPixelLightCount;

s_InsideRendering = false;

}

// Cleanup all the objects we possibly have created

void OnDisable()

{

if( m_ReflectionTexture ) {

DestroyImmediate( m_ReflectionTexture );

m_ReflectionTexture = null;

}

foreach( DictionaryEntry kvp in m_ReflectionCameras )

DestroyImmediate( ((Camera)kvp.Value).gameObject );

m_ReflectionCameras.Clear();

}

private void UpdateCameraModes( Camera src, Camera dest )

{

if( dest == null )

return;

// set camera to clear the same way as current camera

dest.clearFlags = src.clearFlags;

dest.backgroundColor = src.backgroundColor;

if( src.clearFlags == CameraClearFlags.Skybox )

{

Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox;

Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox;

if( !sky || !sky.material )

{

mysky.enabled = false;

}

else

{

mysky.enabled = true;

mysky.material = sky.material;

}

}

// update other values to match current camera.

// even if we are supplying custom camera&projection matrices,

// some of values are used elsewhere (e.g. skybox uses far plane)

dest.farClipPlane = src.farClipPlane;

dest.nearClipPlane = src.nearClipPlane;

dest.orthographic = src.orthographic;

dest.fieldOfView = src.fieldOfView;

dest.aspect = src.aspect;

dest.orthographicSize = src.orthographicSize;

}

// On-demand create any objects we need

private void CreateMirrorObjects( Camera currentCamera, out Camera reflectionCamera )

{

reflectionCamera = null;

// Reflection render texture

if( !m_ReflectionTexture || m_OldReflectionTextureSize != m_TextureSize )

{

if( m_ReflectionTexture )

DestroyImmediate( m_ReflectionTexture );

m_ReflectionTexture = new RenderTexture( m_TextureSize, m_TextureSize, 16 );

m_ReflectionTexture.name = "__MirrorReflection" + GetInstanceID();

m_ReflectionTexture.isPowerOfTwo = true;

m_ReflectionTexture.hideFlags = HideFlags.DontSave;

m_OldReflectionTextureSize = m_TextureSize;

}

// Camera for reflection

reflectionCamera = m_ReflectionCameras[currentCamera] as Camera;

if( !reflectionCamera ) // catch both not-in-dictionary and in-dictionary-but-deleted-GO

{

GameObject go = new GameObject( "Mirror Refl Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox) );

reflectionCamera = go.camera;

reflectionCamera.enabled = false;

reflectionCamera.transform.position = transform.position;

reflectionCamera.transform.rotation = transform.rotation;

reflectionCamera.gameObject.AddComponent("FlareLayer");

go.hideFlags = HideFlags.HideAndDontSave;

m_ReflectionCameras[currentCamera] = reflectionCamera;

}

}

// Extended sign: returns -1, 0 or 1 based on sign of a

private static float sgn(float a)

{

if (a > 0.0f) return 1.0f;

if (a < 0.0f) return -1.0f;

return 0.0f;

}

// Given position/normal of the plane, calculates plane in camera space.

private Vector4 CameraSpacePlane (Camera cam, Vector3 pos, Vector3 normal, float sideSign)

{

Vector3 offsetPos = pos + normal * m_ClipPlaneOffset;

Matrix4x4 m = cam.worldToCameraMatrix;

Vector3 cpos = m.MultiplyPoint( offsetPos );

Vector3 cnormal = m.MultiplyVector( normal ).normalized * sideSign;

return new Vector4( cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos,cnormal) );

}

// Calculates reflection matrix around the given plane

private static void CalculateReflectionMatrix (ref Matrix4x4 reflectionMat, Vector4 plane)

{

reflectionMat.m00 = (1F - 2F*plane[0]*plane[0]);

reflectionMat.m01 = ( - 2F*plane[0]*plane[1]);

reflectionMat.m02 = ( - 2F*plane[0]*plane[2]);

reflectionMat.m03 = ( - 2F*plane[3]*plane[0]);

reflectionMat.m10 = ( - 2F*plane[1]*plane[0]);

reflectionMat.m11 = (1F - 2F*plane[1]*plane[1]);

reflectionMat.m12 = ( - 2F*plane[1]*plane[2]);

reflectionMat.m13 = ( - 2F*plane[3]*plane[1]);

reflectionMat.m20 = ( - 2F*plane[2]*plane[0]);

reflectionMat.m21 = ( - 2F*plane[2]*plane[1]);

reflectionMat.m22 = (1F - 2F*plane[2]*plane[2]);

reflectionMat.m23 = ( - 2F*plane[3]*plane[2]);

reflectionMat.m30 = 0F;

reflectionMat.m31 = 0F;

reflectionMat.m32 = 0F;

reflectionMat.m33 = 1F;

}

}

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20160716

2013-01-06

[][]MMDモデルの読み込みとNavMesh MMDモデルの読み込みとNavMesh - Nao_uの日記 を含むブックマーク はてなブックマーク - MMDモデルの読み込みとNavMesh - Nao_uの日記 MMDモデルの読み込みとNavMesh - Nao_uの日記 のブックマークコメント

MMD for Unityを使って、MMDのモデルとモーションをUnityに取り込む実験。


Defaultシェーダーでインポートするとポリゴンの表裏がおかしくなったような見た目になったので、MMDシェーダーでインポートしてからマテリアルを設定しなおすとうまくいった。

リニアライティングにすると黒い部分の反射率が低すぎたのでGimpのトーンカーブで暗部だけ適当に持ち上げた。足はまだ修正してないので真っ黒。


以前にpmdをfbxに変換して取り込んだ時にはサイズが小さすぎるので補正する必要があったんだけど、MMD for Unityだとデフォルトでこのくらいのサイズになってた。

スカートやネクタイの物理計算がおかしくなってたので少し修正したけど、調整が適当なので動くとめり込んでしまいやすい。


モーションを変換するときは、「Create Asset」のチェックを入れて別のアセットとして出力するとうまく再生できなかったので、チェックを外してPrefavの中に入れるようにすると正常に再生できるようになった。原因は不明。


数体置くと60fpsが維持できなくなってしまった。プロファイラで見るとスクリプトのCPU負荷が高くなっていて、どうやらIKの処理が重くなっているらしい。

CCDIKSolver.csでiterationsのループ回数を1/4くらいに抑えて対処。今回みたいな用途であれば、イテレーション回数を多少減らしても大きな問題にはならないみたい。

ついでにNavMeshをつかってプレイヤーを追いかけてくるようにしてみた。

道路の部分のモデルだけにNavigation staticを設定してbakeするだけで問題なく道路のみを歩いて追いかけてくるようになった。わりと簡単な設定で使えるみたいなのでとても便利。

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20130106

2012-12-25

[][]ラグドールテスト ラグドールテスト - Nao_uの日記 を含むブックマーク はてなブックマーク - ラグドールテスト - Nao_uの日記 ラグドールテスト - Nao_uの日記 のブックマークコメント

Unityでラグドールのテスト。

ついでにWiiリモコンでの射撃テスト、HDRと動的露出補正、SSAOなどで市街地をそれっぽく見せる調整の実験と、動画には撮ってないけどワイヤーによる立体機動アクションの検証も兼ねてる。


Tポーズにしてラグドールを埋め込んだモデルをPrefav化して、弾がヒットした時に、transform以下のすべての姿勢をコピーして、新しいモデルに切り替え。


姿勢生成時に、各関節に速度を追加。ヒットした点からの距離に応じて速度を減衰することで、ヒットした点から力が加わっているように見せる。

// ラグドールの生成 
private void GenerateRagdoll( Transform originalRoot, Vector3 velocity, Vector3 pos)
{
	GameObject ragdoll = (GameObject)Instantiate(mPrefavRagdoll);
	CopyTransformRecursively(originalRoot, ragdoll.transform, velocity, pos);
}

// トランスフォームを再帰的にコピーする 
// ついでに初速の設定も行う(力点との距離が離れたら減衰)
private void CopyTransformRecursively(Transform src, Transform dst, Vector3 velocity, Vector3 pos) 
{
	dst.localPosition = src.localPosition;
	dst.localRotation = src.localRotation;
	
	float maxDist = 0.4f; // 最大距離
	float dist = Vector3.Distance( dst.transform.position, pos );
	float pow = 0.0f;
	if( dist < maxDist ){
		dist /= maxDist;
		pow = Mathf.Pow( dist, 1.0f ); // 線形減衰でも問題なかった
	}
	if (dst.rigidbody) dst.rigidbody.velocity = velocity * pow;
	foreach(var child in src) {
		var srcChild = child as Transform;
		var dstChild = dst.Find(srcChild.name);
		if (dstChild) CopyTransformRecursively(srcChild, dstChild, velocity, pos);
	}
}

吹き飛ぶ前のポーズが両手を合わせた形になっているせいか、手と肩がおかしくなることがある。両手だけ広げる方向に初速を与える、とかで改善できる?

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20121225

2012-12-24

[][]Wii Motion PlusをUnityで使うときのメモ Wii Motion PlusをUnityで使うときのメモ - Nao_uの日記 を含むブックマーク はてなブックマーク - Wii Motion PlusをUnityで使うときのメモ - Nao_uの日記 Wii Motion PlusをUnityで使うときのメモ - Nao_uの日記 のブックマークコメント

GlovePie + PPJoyを使用。GlovePieでリモコンの情報を入力して、PPJoyで作った仮想ジョイスティックに流し込む。

仮想ジョイスティックの値をUnityのInput Settingの設定で通常のジョイスティック入力として受け取る。


PPJoyはver 0.83を使用。Windows7以降ではドライバが正常にインストールできないため、PPJoyをインストール後に、Windowsを未検証ドライバが入れられるテストモードに切り替えて、PPJoySetup-0.8.4.5-early-release.exeでドライバをインストールする必要がある。


Bluetoothの接続はなぜかかなり不安定。最初の接続失敗がかなり多い。2つリモコンを同時につなごうとすると成功しやすいような気がしないでもない。スリープに入るとGlovePieが不安定になって、Windowsを再起動しないとつながりにくくなるような雰囲気も。一度つながってしまえば、しばらくは大丈夫。


GlobePieのスクリプトは下記の通り。アナログ値はPPJoy1.AnalogXで受け渡し。Unity側が0.0~1.0までしか受け取れないみたい(Input Settingで変えられるかも?)

var.Vec_X = Wiimote.MotionPlus.GyroYaw - var.Last_GyroYaw;

var.Vec_Y = Wiimote.MotionPlus.GyroPitch - var.Last_GyroPitch;

var.Vec_Z = Wiimote.MotionPlus.GyroRoll - var.Last_GyroRoll;


var.s0 = Wiimote.MotionPlus.PitchSpeed;

var.s1 = Wiimote.MotionPlus.YawSpeed;

var.s2 = Wiimote.MotionPlus.RollSpeed;


var.Last_GyroYaw = Wiimote.MotionPlus.GyroYaw;

var.Last_GyroPitch = Wiimote.MotionPlus.GyroPitch;

var.Last_GyroRoll = Wiimote.MotionPlus.GyroRoll;


var.ncx = Wiimote1.Nunchuk.JoyX

var.ncy = Wiimote1.Nunchuk.JoyY


var.rx = (var.Last_GyroPitch+180) * (1.0/360.0);

var.ry = (var.Last_GyroYaw+180) * (1.0/360.0);

var.rz = (var.Last_GyroRoll+180) * (1.0/360.0);


while( var.rx < 0 ) var.rx += 1.0;

while( var.rx > 1 ) var.rx -= 1.0;

while( var.ry < 0 ) var.ry += 1.0;

while( var.ry > 1 ) var.ry -= 1.0;

while( var.rz < 0 ) var.rz += 1.0;

while( var.rz > 1 ) var.rz -= 1.0;


var.rrx = var.s0 / 10000 + 0.5;

var.rry = var.s1 / 10000 + 0.5;

var.rrz = var.s2 / 10000 + 0.5;


PPJoy1.Analog3 = var.rrx;

PPJoy1.Analog4 = var.rry;

PPJoy1.Analog5 = var.rrz;


PPJoy1.Analog2 = 0;

PPJoy1.Analog0 = var.ncx;

PPJoy1.Analog1 = -var.ncy;


Keyboard.X = Wiimote.Nunchuk.ZButton;

Keyboard.C = Wiimote1.Minus;

Keyboard.V = Wiimote1.B;

Keyboard.Space = Wiimote1.One;

  • PPJoy1.Analog0 : デフォルトではHorizontal
  • PPJoy1.Analog1 : デフォルトではVertical
  • PPJoy1.Analog2 : 割り当てがうまくいかない?
  • PPJoy1.Analog3~5 : ジョイスティックのZなどに割り当てられた。Unityでは4~6に相当。

UnityのInput Settingで適当な名前を付けて、ジョイパッドからの入力として受け取る。0.0~1.0の値になるように適当に変換して、入力結果を受け取ってから元に戻す。


ジャイロの入力は、角度をそのまま使うとうまくいかなかった。真上に向けて、Z軸回転させてから戻すと元に戻らない。いろいろ試したけどダメだったので、それぞれの軸の速度を受け取るように修正。


今の姿勢行列に、それぞれの軸の回転速度を毎フレーム掛ける。少しづつずれてしまうけど、角度を受け取ってもずれは発生してしまってどこかで補正が必要になるので、こちらの方が素直な実装かも。実際に触ってみたら角度そのままよりも、移動量を増幅した方が気持ちい動きになったので、むしろ好都合だった。


ずれの補正については、センサーバーで正面方向を、重力加速度でピッチとロールを自動補正できるはず。渡せるアナログ値の数の限界に引っかからないかどうかは要確認。

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20121224

2012-12-01

[][]メタボールパーティクル メタボールパーティクル - Nao_uの日記 を含むブックマーク はてなブックマーク - メタボールパーティクル - Nao_uの日記 メタボールパーティクル - Nao_uの日記 のブックマークコメント


Unityのシェーダーとマルチパスレンダリングの使い方の練習を兼ねて、メタボールパーティクルの描画を試してみた。


オフスクリーンに白黒のハイトマップをレンダリングしておいて、2パス目でハイトマップの上下との値の差分から法線マップ化して、反射と屈折した結果を合成して描画。


屈折先テクスチャも別パスで低解像度でレンダリングしていて、反射はキューブマップ+スフィアマップを使用。キューブマップだけだと光沢感が足りなかったので、スフィアマップを足した結果の輝度の高い部分だけを抽出してさらに加算する事で、強引にHDR化してる。


ポストフィルタはブルームとFXAAを使用。地面を上を流れてるときや、粒が小さくなった時の見え方が不自然になってるので、反射や屈折はもう少し工夫する必要がありそう。


  • 今回のハマりポイントのメモ
    • シェーダーで使うテクスチャの数が増えると「Program 'frag_surf', Maximum texture indirection of 4 exceeded; 5 indirections needed to compile program」というエラーが出るので「#pragma target 3.0」が必要になる。

    • カメラを追加してレンダーターゲットテクスチャを追加する事でオフスクリーンレンダリングができる。オブジェクトごとの描画パスの制御はレイヤーで行う。カメラとオブジェクト双方にどのレイヤーを描画するかのチェックを入れる必要があるので注意。ライトも同様。

    • オフスクリーンレンダリングの描画順は、カメラのDepthで制御できる。必要な場合にはカメラのHDRのチェックも忘れずに。

    • 本来は白黒のハイトマップ生成時にデプステストしないと一部描画が破綻するんだけど、描画パスやシェーダーを増やさずに済む上手いやり方が見つからなかったので保留

屈折を別パスで描画するのは背景オブジェクトが増えてくるとと負荷が高くなるので、レンダリングの途中経過やデプスバッファをテクスチャとして再利用したいところだけど、Unityでやるにはどうすれば良いんだろう?

どこかの設定でレンダリング済みのデプスバッファと背景だけを別のカメラに割り当てたりとかができるのかな。

追記:背景のテクスチャをつかって歪めたりするにはGravPassを使うらしい

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20121201

2012-11-21

[][]箱に弾が当たった場所から8分木で再帰的に分割してみる破壊挙動の実験 箱に弾が当たった場所から8分木で再帰的に分割してみる破壊挙動の実験 - Nao_uの日記 を含むブックマーク はてなブックマーク - 箱に弾が当たった場所から8分木で再帰的に分割してみる破壊挙動の実験 - Nao_uの日記 箱に弾が当たった場所から8分木で再帰的に分割してみる破壊挙動の実験 - Nao_uの日記 のブックマークコメント


箱に弾が当たった場所から8分木で再帰的に分割してみる破壊挙動の実験。派手に砕け散ってくれるとちょっと楽しい。


このやり方で地面まで全部再帰的に破壊して掘り下げたりできないものかと期待してたけど、画面に見えてる数個の箱を半壊させるだけでも激しく処理落ちしてる。破壊の絵面としては箱のサイズがもう半分くらい小さいものイメージしてたんだけど、個数が爆発的に増えてしまうので無理っぽかった


穴の中を掘り進むくらいの物量を出したいなら、Mincraftみたいなボクセルでやらないとやっぱり無茶かな。その場合、壊れた箱の当たり判定とかをどう処理するのかが悩ましいところだけど

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20121121

2012-11-08

[]ドット絵で手書きの法線マップを使った光源処理を試してみた ドット絵で手書きの法線マップを使った光源処理を試してみた - Nao_uの日記 を含むブックマーク はてなブックマーク - ドット絵で手書きの法線マップを使った光源処理を試してみた - Nao_uの日記 ドット絵で手書きの法線マップを使った光源処理を試してみた - Nao_uの日記 のブックマークコメント

f:id:Nao_u:20131222113819p:image

ドット絵で手書きの法線マップを使った光源処理を試してみた。


左上がそれぞれアルベドと法線マップで、真ん中が光源計算の結果。カーソルキー左右で光源方向を回転。


平面の多いデザインのものであれば、手作業でもそれなりに見えるものが作れそう。軽く試してみた範囲では思ってたほど非現実的ではなさそうな気配が。慣れると案外いけるかも


16x16くらいのサイズあれば、あらかじめ用意した球を法線マップ化した絵から色を拾いながら作業すればそれなりに現実的なレベルでいける感じ。曲面の多いデザインのものだとだいぶ苦労しそう。


丸いものを表現するなら、何らかの方法(手書き?)で白黒のハイトマップを生成してから変換するほうが楽に作れるかも。ソルバルウの翼とかは1ピクセルごとに法線の向きが変わってたりするので、ハイトマップ化はたぶん無理。こういうのはドット絵として脳内で補完しながら手で書くしかなさそう


ソルバルウの翼みたいにピクセル単位で法線の向きが変わるような絵はバイリニア補完するとおかしな見た目になってしまうので、このくらいの密度のものはポイントサンプリングでないと破綻してしまう


この状態で光源ではなく絵のほうを回転させると、光源計算の見た目的には破綻しないけど、せっかくのドット感が台無しになってしまう。どこかに法線の回転による色変換に対応した、256段階くらいの細さに耐える高精度の回転機能を持つドット絵ツールがあったりしないかな。あまり需要なさそうだけど


Nao_u@Nao_u_

@KidTak たしかに、いろいろテンプレート的なものを用意しておくことで作業効率が上がりそうですね。元絵を拡縮して張り付けるのもよさそうです。この手法の欠点は、圧縮テクスチャにすると劣化しやすいところでしょうか。びっくりするくらいに汚い見た目になってしまいました

Nao_u@Nao_u_

@KidTak そもそも16x16の絵を圧縮しようとかいう時点で根本的に間違ってますね。Unityに取り込んだらデフォルトが圧縮テクスチャになってて、ひどいことになってました

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20121108

2012-11-04

[][]実写合成 実写合成 - Nao_uの日記 を含むブックマーク はてなブックマーク - 実写合成 - Nao_uの日記 実写合成 - Nao_uの日記 のブックマークコメント

昼間の日光が入る状態で基本機能だけでライティングを調整。やはりちょっと無理がある感じ


夜の室内照明で撮影。髪の毛の色が変わってしまった。

ソフトフォーカスを入れてみた。それっぽく見せるには質感そのものより、AA、AO、ライティングが重要っぽい感じが。



さぽてんミクを足してみた。体は未調整。

ライティングの複雑さが足りない感じ。やっぱりキューブマップは欲しい。

やはりある程度のジオメトリの複雑さがあったほうが奇麗。スペキュラーにノイズを入れると良くなりそう。


本物の影を参考に、影付きの平行光源を何灯か使うべきだった。でないと複雑な影が落ちない。一灯で無理してあわせようとしたのが失敗か。

部屋の蛍光灯くらいの距離感だと、人間サイズのものが立っただけで地面の影は恐ろしくボケてる。これを合わせるのはかなり大変そう。


あと、環境を変えて撮るだけで髪の毛の色がこんなに違ってくるとは思わなかった。ミクの髪の毛の色は青から緑までバリエーションがあるけど、同じ実物を写真に撮った時に出る差の範囲であれば違和感がなかったりとかするのかな


Webカメラでの撮影だと、肉眼では普通に見える環境でも、ライティングをきっちりやらないと飛んだり潰れたりで見れたものではなくなるのは何とかならないものか。どこに合わせるのがいいのかな


既存のシェーダーを使った強引な目合わせじゃなくて、周りの環境をHDR撮影してキューブマップにしたうえで、もうちょっと物理的なパラメータに近いシェーダーを使ったらどのくらい違いが出るのかも試してみたい

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20121104

2012-09-14

[][]2DのBGエディタ 2DのBGエディタ - Nao_uの日記 を含むブックマーク はてなブックマーク - 2DのBGエディタ - Nao_uの日記 2DのBGエディタ - Nao_uの日記 のブックマークコメント


Unityのエディタ拡張で、2DのBGエディタを作成中。

なんとか必要な要素の実装方法に目処がたってきたところ。ゲーム本編と似た感覚でUIを作れるのは便利。Unityはゲームじゃなくてツール作成環境としても使えそう。

それにしても、こういうエディタ内のデータってどう保存するのがいいんだろう?今回はBGマネージャの子供OBJの配置情報から毎回再取得することに。

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20120914

2012-08-26

[][]Unityで市街地をHDRで描画してみるテスト Unityで市街地をHDRで描画してみるテスト - Nao_uの日記 を含むブックマーク はてなブックマーク - Unityで市街地をHDRで描画してみるテスト - Nao_uの日記 Unityで市街地をHDRで描画してみるテスト - Nao_uの日記 のブックマークコメント

Unityで市街地をHDRで描画してみるテスト。写真ベースのテクスチャだとアルベドが高すぎておかしな絵になってる事が多かった。ライティングやマテリアルを延々と調整してみてたけど、まだ違和感がたくさん残ってる。調整難しい


Nao_u@Nao_u_

.@masakam1 シェーダーはデフォルトで入っている Refrective/Bumped Speculer をそのまま使いました。キューブマップは、くっきりしたものとボケたものの2種類を作って、素材ごとに張り分けています。


カメラの設定でレンダリング先をHDRにして、ディファードレンダリングでBeastで焼き付けたライトマップ+平行光の影で描画しています。あとはライトの強さやマテリアル(特にアルベド)のパラメータをいろいろ微調整して、今の形に落ち着きました。


ほぼ全てのマテリアルに環境マップを適用した事と、HDRのリニアライティングに合わせてアルベドを適切な値に調整した事が、特に見た目に大きく寄与したような印象でした。まだいろいろと違和感のある部分が残ってはいますが。


さっきのデモをちょっとだけ更新。空の色に合わせて天空光を再設定したり、ライトマップを焼くときの反射回数増加のなど、細かくクオリティUP。beastはCPUの8スレッドをほぼ100%使い切ってくれてるのに、それでも焼くのには時間がかかってしまうので心理的にも焼き直すのは結構面倒

Nao_u@Nao_u_

@masakam1 背景はアセットストア(http://t.co/pxvFPTkd)から購入して、そこに入っていたデモ用の地形をほぼそのまま使用しています。建物や道路は一個づつ別のプレハブになっていました。あと、それぞれのテクスチャに法線マップも含まれていたのは、嬉しい誤算でした

Nao_u@Nao_u_

@rockout77 そうですね。ちょっと建物を動かしたり、空の色と合わせようと微調整するだけでも10分程度の待ち時間がかかったりすると試行錯誤しにくい、という面はありそうです。最近のリアルタイムGIの流れは、このあたりを踏まえての効率化にもつながりそうでしょうか。

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20120826

2012-08-18

[][]『航空雷撃戦 Ver0.1』 『航空雷撃戦 Ver0.1』 - Nao_uの日記 を含むブックマーク はてなブックマーク - 『航空雷撃戦 Ver0.1』 - Nao_uの日記 『航空雷撃戦 Ver0.1』 - Nao_uの日記 のブックマークコメント

Unityで魚雷で空母を攻撃するフライトシミュレータもどきを作ってみた。上記リンクから、ブラウザで動作可能。

(要Unity Web Player)


航空魚雷の性質上、ある程度離れたところから撃たないといけないため、相手が動いてると命中させるのが難しい。リトライすることで編隊の機数が増えて、一応爆撃もできる。


画面右下の小さい枠内には魚雷視点の映像が映っていて、魚雷は着水後にしばらく水面下を深く潜るため、一時的に姿が見えなくなった後、航跡を残しながら目標に向かう。



航空雷撃ってどのくらい難しいのかを試してみたくなったので作ってみた。フライトシミュレータとしてはかなり適当な挙動だけど、一応は敵艦が約30ノットで魚雷は40ノット、航空機の速度は時速350km/hと、それぞれの速度比は実際のものにだいたい合わせてみた。


敵艦に近づいてしまうと猛烈な対空砲火が待っているため、普段は距離800〜1000mで発射した後に、敵艦前方に退避するらしい。実際、距離1000mだとまだまだ船とは大きく離れているため、移動目標の先を読んで当てるのはかなり難しい。


真珠湾攻撃のときは湾の水深が浅いため、距離300m高度5mからの雷撃だったそうで。ちょっと危険を感じるくらいの低高度ではあるものの、こちらは奇襲なので停止目標な上に相手からの反撃も非常に少ないので、命中率はかなり高そう。





当時の回顧録を読んでいると「見事な操船で左右に上手く舵を切って、魚雷を回避した」みたいな記述がよく出てきて、船って相当に鈍重な印象があるので、あんなものを見てから避けられるのか?という疑問があったんだけど、実際の速度比で動くものを作ってみて、納得した。


まず、魚雷の水中での速度は意外と遅く、直線的に動いてる敵艦を狙うだけでもかなり難しい。また、魚雷発射時にはかなりの時間まっすぐに飛ばないといけないうえに、発射後に命中まで数十秒から分単位の時間があるので、熟練した艦長であれば雷撃機の軌道や魚雷の航跡をみてうまく左右に舵を切って回避する余地は十分にありそう。


軍艦でも速度の速いものは全力航行すると30ノットを超える速度が出て、時速に直すと60km/hくらいになるので、想像してたよりはずっと速い。20ノット前後しか出ない鈍足艦と最高速が35ノットを超える高速艦では、爆撃や魚雷相手の回避能力もかなり違ってくるんじゃないか、と思った。

トラックバック - http://game.g.hatena.ne.jp/Nao_u/20120818