HGIMG4で広角透視モードでも「テクスチャが常に画面に正面を向く」シェーダーを実装したいです。
イメージとしては、2Dのテクスチャが変形することなく、オブジェクトの輪郭にそって、
そのまま画面にくりぬかれたような単純なNPR系シェーダーです。
以下のシェーダーはカメラが平行投影モードである時はイメージ通りのマテリアルなのですが、
広角透視モードではテクスチャのUVがカメラと逆向きへ回ってしまって意図した表現になりません。
これを広角透視モードでも、平行投影モードと同様の効果を持つシェーダーにしたいです。
サンプルファイル
http://youdaizone.webcrow.jp/sc_shader_test.zip
sc_dev.vert
// 解説 // ・マテリアルへテクスチャを平面的に貼り付けたようなNPR系マテリアル // 現象と問題点 // ・カメラが平行投影モードの場合はこれで問題ないが、 // 広角透視モードの場合はUVがカメラと逆向きへ回ってしまう // ・さらに広角透視モードの場合はマテリアルの平面性も失われてしまう // ・これを広角透視モードでも、平行投影モードと同様の効果を持つシェーダーにしたい //------------------------------------------------------------------------------ // attribute attribute vec4 a_position; attribute vec3 a_normal; //------------------------------------------------------------------------------ // uniform // world uniform mat4 u_worldViewProjectionMatrix; // inverse uniform mat4 u_inverseTransposeWorldViewMatrix; //------------------------------------------------------------------------------ // varying varying vec2 v_uv; //------------------------------------------------------------------------------ // main void main() { // オブジェクトの法線の向きをu_inverseTransposeWorldViewMatrixへ曲げる vec3 uv = (u_inverseTransposeWorldViewMatrix * vec4(a_normal, 0.0)).xyz; v_uv = uv.xy * 0.5 + 0.5; // UV座標をセンタリング処理 gl_Position = u_worldViewProjectionMatrix * a_position; }
sc_dev.frag
//------------------------------------------------------------------------------ // quality #if defined(OPENGL_ES) || defined(GL_ES) #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif #endif //------------------------------------------------------------------------------ // uniform uniform sampler2D u_screenTexture; //------------------------------------------------------------------------------ // varying varying vec2 v_uv; //------------------------------------------------------------------------------ // main void main() { vec4 buf = texture2D(u_screenTexture, v_uv); gl_FragColor = buf; }
sphia.material
material screenTexture { u_worldViewProjectionMatrix = WORLD_VIEW_PROJECTION_MATRIX u_inverseTransposeWorldViewMatrix = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX sampler u_screenTexture { mipmap = false wrapS = REPEAT wrapT = REPEAT minFilter = NEAREST magFilter = NEAREST } renderState { cullFace = true depthTest = true } technique { pass { vertexShader = sc_dev.vert fragmentShader = sc_dev.frag } } } material bokashi : screenTexture { sampler u_screenTexture { path = res/checker.png } }
test.hsp
#include "hgimg4.as" #module ; 画面比率4:3(アスペクト比 1.333333333333333)用平行投影変換 #defcfunc ortho_43 double p return (p*p*p)/3.0 ; 背景色描写 #deffunc bgdraw int u_color pos 0, 0 rgbcolor u_color boxf return #global gosub *reset ; 初期化処理 gosub *setup_camera gosub *setup_object goto *main *reset w = 800 ; スクリーン横幅 h = 600 ; スクリーン縦幅 screen 0, w, h setreq SYSREQ_VSYNC, 1 ; VSYNC同期ON setreq SYSREQ_CLSMODE, 0 ; 背景消去OFF setreq SYSREQ_LOGWRITE, 0 ; log出力を抑制 gpreset return *setup_camera aspect = 1.333333333333333 ; カメラを平行投影モードか、広角透視モードで作成するスイッチ ; s = 0 : 平行投影モード ; s = 1 : 広角透視モード s = 0 if s == 0 { title "平行投影モード" ; カメラを平行投影モードで作成 gpcamera camID, 8.0, ortho_43(aspect), 0.1, 50.0, 1 } else { title "広角透視モード" ; カメラを広角透視モードで作成 gpcamera camID, 51.48, aspect, 0.1, 100.0, 0 } gpusecamera camID setpos camID, 8.0 * 0.5, 0.0, 8.0 return *setup_object gpbFileName = "sphia" ; .GPBファイルの名前 gpnull objID gpload objID, "res/" + gpbFileName if objID == -1 : dialog "error", 1, "gpload error" : end pX = 0.0 pY = 0.0 pZ = 0.0 setpos objID, pX, pY, pZ repeat 4 pX += 2.0 gpclone cloneID.cnt, objID setpos cloneID.cnt, pX, pY, pZ loop return *main redraw 0 bgDraw 0x000080 gpDraw redraw 1 await 1000 / 60 goto *main
(sphiaはbokashiという名前のマテリアルを持ったGPBファイルだと思って下さい。
checker.pngはそのテクスチャ用画像ファイルです)
test.hspを実行すると、平行投影モードでイメージ通りのマテリアルが表現されています。
ソースの中の変数sを1にすると広角透視モードになり、マテリアルのUVの正面性が失われているのが分かると思います。
おそらく広角透視モードでは座標変換が、平行投影モードとは違うものが必要なのではないかと思います。
座標変換している部分は、.vertの以下の部分です。
// オブジェクトの法線の向きをu_inverseTransposeWorldViewMatrixへ曲げる vec3 uv = (u_inverseTransposeWorldViewMatrix * vec4(a_normal, 0.0)).xyz;
アドバイスお願い致します。