2010年6月30日水曜日

NyMmdに影行列をやってみる

追記(2010/07/05): ARTK_MMDのコードではrenderForShadowってのがあって影行列適応時はこっちで描いてた.



動機
前回の続き.


やってみた
0. 以前の状況からスタート.


1. jp.nyatla.nymmd.test.MmdTest.javaの編集
1.1 MmdTestクラスにフィールドの追加
private float[] ShadowMatrix_ary = new float[16]; //影行列4x4


1.2 MmdTestクラスにメソッド追加
 private static void CalcShadowMatrixf(float[] planeAry, float[] lightPosAry,
float[] shadowMatrixAry) {
float dot = 0.0f;
for (int i = 0; i < 4; i++) {
dot += planeAry[i] * lightPosAry[i];
}

for (int loopY = 0; loopY < 4; ++loopY) {
for (int loopX = 0; loopX < 4; ++loopX) {
shadowMatrixAry[loopY * 4 + loopX] = -planeAry[loopY]
* lightPosAry[loopX];
if (loopX == loopY) {
shadowMatrixAry[loopY * 4 + loopX] += dot;
}
}
}
}

1.3 MmdTestクラス'public void init(GLAutoDrawable drawable);'メソッドの最後, 'return;'直前に以下を追加.
  {
_gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
float plane_ary[] = { 0.0f, 1.0f, 0.0f, 0.0f };
_gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, fLightPos, 0);
fLightPos[2] *= -1.0f;
CalcShadowMatrixf(plane_ary, fLightPos, ShadowMatrix_ary);
}

1.4 MmdTestクラス'public void display(GLAutoDrawable drawable):'メソッドの'this._render.render();'直後に以下を追加.
   {
_gl.glDisable(GL.GL_LIGHTING);
_gl.glDisable(GL.GL_LIGHT0);
_gl.glPushMatrix();
_gl.glMultMatrixf(ShadowMatrix_ary, 0);
_gl.glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
//_gl.glColor4f(0.4f, 0.4f, 0.4f, 1.0f);
this._render.render();
_gl.glPopMatrix();
_gl.glEnable(GL.GL_LIGHTING);
_gl.glEnable(GL.GL_LIGHT0);
}


2. 実行
2.1 “VM arguments:に'-Djava.library.path=./lib'を入れる”を忘れずに.
2.2 pmd, vmdの順でファイルを指定する.


結果
できた(pmd: '初音ミク'(MMD添付モデル),vmd: '【MMD】みくみくにしてあげる♪【してやんよ】【Project DIVA】' 感謝).


モデルによっては明るめの色(0.4, 0.4, 0.4)を影に指定しても問題なさそう.テクスチャの有無が問題かしら?影に色がついてしまうモデルもあった.
内部で指定しているのだろうか?'_gl.glDisable (GL.GL_TEXTURE_2D);'とかは意味なかったな.はて?


追記
jp.nyatla.nymmd.MmdPmdRenderGL.javaのMmdPmdRenderGLクラスにあるrenderメソッド内でテクスチャの有無でgl.glEnable(GL.GL_TEXTURE_2D);を呼び出している箇所があった.
そこでこのメソッドの呼び出し時のGL_TEXTURE_2Dの状態に応じて,実行するかどうか判定するようにした.
で.影の描画時にはコード側でglDisable(GL.GL_TEXTURE_2D)としてからrenderメソッドを呼ぶようにしてあげた.
これで前述の影に色がついてしまうモデルでも色がつかず,指定した影の色での描画となることができた(pmd: '初音ミクmetal'(MMD添付モデル),vmd: '【MMD】みくみくにしてあげる♪【してやんよ】【Project DIVA】' 感謝).


jp.nyatla.nymmd.MmdPmdRenderGL.java MmdPmdRenderGLクラス renderメソッド内
   boolean b = gl.glIsEnabled(GL.GL_TEXTURE_2D);
if (条件 && b == true) {
// テクスチャあり

} else {
// テクスチャなし

}
…描画他…
if (b) {
gl.glEnable(GL.GL_TEXTURE_2D);
}



jp.nyatla.nymmd.test.MmdTest.java MmdTestクラス displayメソッド内
   {
_gl.glDisable(GL.GL_LIGHTING);
_gl.glDisable(GL.GL_LIGHT0);
_gl.glDisable(GL.GL_TEXTURE_2D);
_gl.glPushMatrix();
_gl.glMultMatrixf(ShadowMatrix_ary, 0);
//_gl.glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
_gl.glColor4f(0.4f, 0.4f, 0.4f, 1.0f);
this._render.render();
_gl.glPopMatrix();
_gl.glEnable(GL.GL_LIGHTING);
_gl.glEnable(GL.GL_LIGHT0);
_gl.glEnable(GL.GL_TEXTURE_2D);
}


影行列をJOGLでやってみた

動機
これに影とかつけたいなー.
OpenGLでどうやるんだろうー.影行列っていうのをつかうみたいだなー.
解説サンプルが載ってる.
JOGLでやってみよう!.


感謝
解説サンプル掲載ありがとうございます!



やってみた
JOGLの環境については以前のモノ等を参照してください.

上記サイトのサンプルの設定
光源位置(7, 5, 5)、平面の方程式 y + 1 = 0とした時に、
物体を描画し、平面「y + 1 = 0」に影を描画せよ。

これをJOGLでやってみたい.


ただここでは描画対象を球に変更し,光源を高さ(y)5の平面上の半径8の円周上でグルグル回しています.
tの上限とか見てないぐらい'てきとー'

コード
import java.awt.Frame;
import java.awt.Insets;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.FloatBuffer;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.glu.GLUquadric;
import com.sun.opengl.util.FPSAnimator;

public class Sample implements GLEventListener {

private final static int SCREEN_X = 640;
private final static int SCREEN_Y = 480;
float ShadowMatrix_ary[] = new float[16];
float plane_ary[] = { 0.0f, 1.0f, 0.0f, 1.0f };
float light_pos_ary[] = { 8.0f, 5.0f, 0.0f, 1.0f };
FloatBuffer light_pos = FloatBuffer.wrap(light_pos_ary);
float OffsetFactor = 1.0f;
float OffsetUnit = 0.0f;
float floorcolor_ary[] = { 0.8f, 0.8f, 0.8f, 1.0f };
FloatBuffer floorcolor = FloatBuffer.wrap(floorcolor_ary);
float red_ary[] = { 0.8f, 0.2f, 0.2f, 1.0f };
FloatBuffer red = FloatBuffer.wrap(red_ary);
private FPSAnimator animator;
private double t;

public Sample() {
Frame frame = new Frame("Sample JOGL");
frame.setVisible(true);
Insets ins = frame.getInsets();
frame.setSize(SCREEN_X + ins.left + ins.right, SCREEN_Y + ins.top
+ ins.bottom);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
GLCanvas canvas = new GLCanvas();
frame.add(canvas);
canvas.addGLEventListener(this);
canvas.setBounds(ins.left, ins.top, SCREEN_X, SCREEN_Y);
}

public void init(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
{
gl.glEnable(GL.GL_LIGHTING);
gl.glEnable(GL.GL_LIGHT0);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light_pos);
gl.glEnable(GL.GL_DEPTH_TEST);
}
CalcShadowMatrixf(plane_ary, light_pos_ary, ShadowMatrix_ary);
animator = new FPSAnimator(drawable, 60);
animator.start();
t = 0.0f;
}

public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) {
GL gl = drawable.getGL();
GLU glu = new GLU();
gl.glViewport(0, 0, w, h);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(30.0, (double) w / (double) h, 1.0, 100.0);
gl.glMatrixMode(GL.GL_MODELVIEW);
}

public void display(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
GLU glu = new GLU();
light_pos_ary[0] = Double.valueOf(9.0 * Math.cos(t)).floatValue();
light_pos_ary[2] = Double.valueOf(9.0 * Math.sin(t)).floatValue();
t+= 0.01;
CalcShadowMatrixf(plane_ary, light_pos_ary, ShadowMatrix_ary);

gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
glu.gluLookAt(6.0, 7.0, 8.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
float xAngle = 0f;
float yAngle = 0f;
gl.glRotated(xAngle, 1, 0, 0);
gl.glRotated(yAngle, 0, 1, 0);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light_pos);
{
// 床を描画
gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
gl.glPushMatrix();
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, floorcolor);
gl.glPolygonOffset(OffsetFactor, OffsetUnit);
if (true) {
gl.glTranslatef(0, -1, 0);
gl.glRotatef(90, -1, 0, 0);
gl.glNormal3d(0, 0, 1);
gl.glRectf(-5, 5, 5, -5);
}
gl.glPopMatrix();
gl.glDisable(GL.GL_POLYGON_OFFSET_FILL);
}
{
// 通常の描画
gl.glPushMatrix();
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, red);
if (true) {
GLUquadric qobj0 = glu.gluNewQuadric();
glu.gluQuadricDrawStyle(qobj0, GLU.GLU_FILL);
glu.gluQuadricNormals(qobj0, GLU.GLU_SMOOTH);
glu.gluSphere(qobj0, 0.8f, 20, 20);
// glutSolidTeapot(1);
}
gl.glPopMatrix();
}
{
// 影の描画
gl.glDisable(GL.GL_LIGHTING);
gl.glDisable(GL.GL_LIGHT0);
gl.glPushMatrix();
gl.glMultMatrixf(ShadowMatrix_ary, 0);
gl.glColor3d(0, 0, 0);
if (true) {
GLUquadric qobj0 = glu.gluNewQuadric();
glu.gluQuadricDrawStyle(qobj0, GLU.GLU_FILL);
glu.gluQuadricNormals(qobj0, GLU.GLU_SMOOTH);
glu.gluSphere(qobj0, 0.8f, 20, 20);
// glutSolidTeapot(1);
}
gl.glPopMatrix();
gl.glEnable(GL.GL_LIGHTING);
gl.glEnable(GL.GL_LIGHT0);
}
}

void CalcShadowMatrixf(float[] planeAry, float[] lightPosAry,
float[] shadowMatrixAry) {
float dot = 0.0f;
for (int i = 0; i < 4; i++) {
dot += planeAry[i] * lightPosAry[i];
}
for (int loopY = 0; loopY < 4; ++loopY) {
for (int loopX = 0; loopX < 4; ++loopX) {
shadowMatrixAry[loopY * 4 + loopX] = -planeAry[loopY]
* lightPosAry[loopX];
if (loopX == loopY) {
shadowMatrixAry[loopY * 4 + loopX] += dot;
}
}
}
}


@Override
public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {
//
}

public static void main(String[] args) {
new Sample();
}
}




結果


2010年6月21日月曜日

GWTFusen改めTwFusenでMobileSafari対応としてその他 修正したところ.

前回の記事で書きませんでしたがMobileSafari対応として,setItem時に'QUOTA_EXCEEDED_ERR'が出る問題を修正してました.
調べて下記のPOSTが見つかるまではあれこれ悩んでました.

結局getItemして値がある場合は,setItemする前に一度removeItemするようにしましたね.
なので備忘録.

http://twitter.com/dankogai/status/15065254349
Safari 4 iPad のバグ発見? window.localStorage.setItem() で値を上書きしつづけると QUOTA_EXCEEDED_ERR がすぐ出るようになる。 removeItem() してからsetItem() で回避できるがなんか変…


感謝.