HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2021
1128
youdaiHGIMG4で広角透視モードでも「テクスチャが常に画面に正面を向く」シェーダーを実装したい3解決


youdai

リンク

2021/11/28(Sun) 02:35:06|NO.94551

●HGIMG4で広角透視モードでも「テクスチャが常に画面に正面を向く」シェーダーを実装したい

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;

アドバイスお願い致します。



この記事に返信する


砂時 計

リンク

2021/11/28(Sun) 18:02:32|NO.94556

球面モデルは割と特別な条件なので
他のモデルに応用効かない可能性がありますが
一旦そこはおいていて今の方法を踏襲する形で考えてみました。
ご参考まで。

透視投影は平行投影とは異なり、さらにカメラ位置から点までの角度がついているので
その分を法線に加算してあげました。

sc_dev.vert

//------------------------------------------------------------------------------ // attribute attribute vec4 a_position; attribute vec3 a_normal; //------------------------------------------------------------------------------ // uniform // world uniform mat4 u_worldViewProjectionMatrix; // inverse uniform mat4 u_inverseTransposeWorldViewMatrix; uniform mat4 u_worldViewMatrix; uniform mat4 u_projectionMatrix; //------------------------------------------------------------------------------ // varying varying vec2 v_uv; //------------------------------------------------------------------------------ // main void main() { // オブジェクトの法線の向きをu_inverseTransposeWorldViewMatrixへ曲げる vec3 uv = (u_inverseTransposeWorldViewMatrix * vec4(a_normal, 0.0)).xyz; if (u_projectionMatrix[3][3] == 0.0) { vec3 r = normalize((u_worldViewMatrix * a_position).xyz); // Y軸回り vec3 s = vec3(-r.z * uv.x + r.x * uv.z, uv.y, r.x * uv.x - r.z * uv.z); // X軸回り vec3 t = vec3(s.x, - r.z * s.y + r.y * s.z, - r.y * s.y - r.z * s.z); v_uv = t.xy * 0.5 + 0.5; } else { v_uv = uv.xy * 0.5 + 0.5; // UV座標をセンタリング処理 } gl_Position = u_worldViewProjectionMatrix * a_position; }

それっぽくはなりますがカメラが近かったり広角であるほど
外側の歪みが大きくなりますので
正規化デバイス座標系での指定も検討してみるといいかもしれません。



砂時 計

リンク

2021/11/29(Mon) 20:13:51|NO.94564

モデルの原点からの半径をざっくり指定する方法でも実装してみました。
半径 radius の他に画面アスペクト比 aspectw を指定する必要があります。

sc_dev.vert

//------------------------------------------------------------------------------ // attribute attribute vec4 a_position; attribute vec3 a_normal; //------------------------------------------------------------------------------ // uniform // world uniform mat4 u_worldViewProjectionMatrix; // inverse uniform mat4 u_inverseTransposeWorldViewMatrix; uniform mat4 u_worldViewMatrix; uniform mat4 u_projectionMatrix; //------------------------------------------------------------------------------ // varying varying vec4 v_position; varying vec4 v_node; varying vec4 v_radius; float radius = 1.0; //------------------------------------------------------------------------------ // main void main() { v_position = u_worldViewProjectionMatrix * a_position; v_node = u_worldViewProjectionMatrix * vec4(0.0, 0.0, 0.0, 1.0); v_radius = u_projectionMatrix * vec4(radius, 0.0, v_node.z, 1.0); gl_Position = v_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 vec4 v_position; varying vec4 v_node; varying vec4 v_radius; float aspectw = 800.0 / 600.0; //------------------------------------------------------------------------------ // main void main() { vec4 p = v_position / v_position.w; vec4 q = v_node / v_node.w; vec4 r = v_radius / v_radius.w; float rate = 1.0 / r.x; vec2 uv = vec2((p.x - q.x) * rate * 0.5 + 0.5, (p.y - q.y) * rate / aspectw * 0.5 + 0.5); vec4 buf = texture2D(u_screenTexture, uv); gl_FragColor = buf; }

奥行の位置で、半径radiusがどのくらいの大きさになるかを見積もって
その幅で割って uv幅 を調整しています。



youdai

リンク

2021/11/30(Tue) 12:50:01|NO.94573

アドバイスありがとうございます。

> 正規化デバイス座標系での指定も検討してみるといいかもしれません。

という案を頂いたので、正規化デバイス空間を使って「テクスチャが常に画面に正面を向く」シェーダーを自分なりに作りました。

screen.vert


//------------------------------------------------------------------------------ // attribute attribute vec4 a_position; attribute vec3 a_normal; //------------------------------------------------------------------------------ // uniform // world uniform mat4 u_worldViewProjectionMatrix; // inverse uniform mat4 u_inverseTransposeWorldViewMatrix; // アスペクト比 uniform float u_aspect; //------------------------------------------------------------------------------ // varying varying vec2 v_uv; // 正規デバイス空間化 // wvmp = u_worldViewProjectionMatrix // position vec4 standardDeviceSpace(mat4 wvpm, vec4 position) { vec4 pos = wvpm * position; float w = pos.w; pos = vec4(pos.x / w, pos.y / w, pos.z / w, w); return pos; } //------------------------------------------------------------------------------ // main void main() { vec4 screenPosition = standardDeviceSpace(u_worldViewProjectionMatrix, a_position); // xにアスペクトを掛けた上で、座標をセンタリング処理 v_uv = vec2((screenPosition.x * u_aspect) + 0.5, screenPosition.y + 0.5); gl_Position = u_worldViewProjectionMatrix * a_position; }

screen.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_worldMatrix = WORLD_MATRIX u_worldViewMatrix = WORLD_VIEW_MATRIX u_worldViewProjectionMatrix = WORLD_VIEW_PROJECTION_MATRIX u_inverseTransposeWorldViewMatrix = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX u_inverseTransposeWorldMatrix = INVERSE_TRANSPOSE_WORLD_MATRIX u_projectionMatrix = PROJECTION_MATRIX u_cameraWorldPosition = CAMERA_WORLD_POSITION u_cameraViewPosition = CAMERA_VIEW_POSITION u_viewMatrix = VIEW_MATRIX u_viewProjectionMatrix = VIEW_PROJECTION_MATRIX u_matrixPalette = MATRIX_PALETTE u_aspect = 1.333333333333333 sampler u_screenTexture { mipmap = false wrapS = REPEAT wrapT = REPEAT minFilter = NEAREST magFilter = NEAREST } renderState { cullFace = true depthTest = true depthWrite = false } technique { pass { vertexShader = screen.vert fragmentShader = screen.frag } } } material bokashi : screenTexture { sampler u_screenTexture { path = res/checker.png } }

これでも「テクスチャが常に画面に正面を向く」シェーダーを実装することができました。

砂時計さんの2つ目のシェーダーも大変すばらしいと思います。
平面性のシェーダーというだけでも色々種類があることに今回気がつくことができました。

アドバイス大変参考になりました。
ありがとうございました。



ONION software Copyright 1997-2023(c) All rights reserved.