マテリアルの設定変更
目次
はじめに
前回(パラメータ)こんなことを書きました。
スマンありゃ ウソだった。
マテリアルの設定を変えれば、透過したときの色の混ぜ方(ブレンディング)を変更できたり、Zテスト関連の設定を変えることで描画順に依存しない表現も可能なようです。(と、先日メールでご指摘いただきました。知らない情報だったのでありがたかったです。よくわからんと思ってマニュアルは読み飛ばしてました。)
マテリアルと言うと、テクスチャの設定などがよく使うところだと思いますが、他にもレンダリングに関する設定を変更できました。
materialファイルを書き換えてみる
materialファイルでいろいろできるようなので、ちょっと確認してみます。
使うサンプルは、HSP3付属の「\sample\hgimg4\tamane2.hsp」です。歩く珠音が表示されるものです。不透明だと効果がわからないものもあるので、適当な行に次の一行を書き足して半透明表示にしておきます。
gpsetprm id_model, PRMSET_ALPHA, 127
実行すると珠音(たまね)が半透明表示されます。腕が見えたりと少し透けすぎな部分もありますが、それなりにいい感じです。 確認したらEscキーを押して終了しておきます。
「\res\tamane2.material」をテキストエディターで開いて書き換えます。戻せなくなると困るので、バックアップを取っておきましょう。 materialファイルは、GLSLという言語で書かれています。HSP3とは文法がまったく違うのでご注意ください。
書き換えるのは、以下の範囲。上の方なので、すぐに見つかりました。
renderState
{
cullFace = true
depthTest = true
blend = true
blendSrc = SRC_ALPHA
blendDst = ONE_MINUS_SRC_ALPHA
}
今の設定は、次のようになっています。
変数 | 設定内容 | 現在の設定値 |
---|---|---|
cullFace | カリング | 有効 |
depthTest | Zテスト | 有効 |
blend | ブレンド | 有効 |
blendSrc | ブレンド元 | 書き込み元α |
blendDst | ブレンド先 | 書き込み元α(反転値) |
試しにcullFace
を変えてみます。
この変数はカリング(隠面消去/隠面除去)の有効無効を設定できます。
有効にすると裏面を描画しません。
通常ポリゴンの裏面は、内側に配置するため隠れて見えません。
見えない部分を描画しなければ、その分処理が軽くなります。
また見えないほうが、演出上都合がいい場合もあります。
ここではカリングを無効にして、裏面も表示するようにしてみます。
cullFace = false
に書き換えて、materialファイルを上書き保存。tamane2.hspを実行すると、裏面が見えるようになります。 半透明表示じゃなくても、スカートが裏面から見えるようになるといった効果があります。
同様にdepthTest
も変更してみます。
この変数は、Zテスト(深度テスト)を実施する/実施しないを設定できます。
Zテストをざっくり説明すると、重なっている面どうしの深度(カメラからの距離)を比較テストして、合格したものだけ描画するという機能です。(通常は、カメラに近い方だけを合格とします。判定基準は変更可能。)
depthTest
をfalse
に設定すると、Zテストを実施しません。すべての面がテストに合格したものとして描画されます。
スカートで隠れている足も表示されるようになります。
cullFace = true
depthTest = false
cullFace
、depthTest
をtrue
に戻して、depthWrite = false
を追加するとどうでしょうか。Zバッファ書き込みを無効にします。
cullFace = true
depthTest = true
depthWrite = false
depthTest
をfalse
にしたときと同じような描画結果になりました。
深度書き込みをしない=テストするための深度情報がないため、本来隠れるはずの面はテストされずに描画されます。
ZテストとZバッファ書き込み
この結果だけだと、depthTest
(Zテスト)とdepthWrite
(Zバッファ書き込み)の違いがよくわかりませんね。
珠音(たまね)とカメラの間に板を置くと違いが出ます。
gpplate id_plate, 10, 60, $303050
setpos id_plate, 5, 0, 10
を適当な場所に追加すると板が出現します。
Zテストだけを無効(depthTest = false
)にすると、珠音の全身が表示されます。板を無視して描画されました。ノードオブジェクト内だけでなく、板(他のオブジェクト)ともZテストをしていないようです。
Zバッファ書き込みだけを無効(depthWrite = false
)にすると、珠音は板で隠れました。板とZテストしているようです。
こちらは1つのモデル内で完結するものみたいですね。
Unityなどでも、入り組んだモデルを半透明表示する場合は、Zバッファ書き込みを有効にするようです。
深い理屈はよくわかりませんが、ZテストとZバッファ書き込みには、こういう違いがあるようです。
gpmatstate
materialファイルを書き換える以外にも、マテリアルの設定を変更する方法があります。
gpmatstate
、gpmatprmt
命令を使う方法です。
マテリアルの設定を変更するタイミングは、オブジェクト生成前と生成後の2パターンがあります。 実装方法は、次のような手順です。
オブジェクト生成前にマテリアルの設定を変える場合
gptexmat
またはgpcolormat
でマテリアルを作成。gpmatstate
でマテリアルの設定を変更。gpbox
やgpplate
の引数としてマテリアルIDを渡してノードオブジェクトを作る。
オブジェクト生成後にマテリアルの設定を変える場合
- ノードオブジェクトを作成しておく。(gpboxやgploadなど)
- ノードオブジェクトが使用しているマテリアルIDを
gpnodeinfo
で取得する。 gpmatstate
でマテリアルの設定を変更。
オブジェクト生成前なら、共通のマテリアルを1つ準備するだけですみます。しかし、後から設定を変更することはできません。 一方、オブジェクト生成後の場合は、オブジェクト1つ1つのマテリアルを書き換える必要がありますが、設定を動的に変更できます。つまり、メインループ内の任意のタイミングでマテリアルの設定を変更できます。 この辺の動きを正しく理解するには、マテリアルの取り扱いについて理解する必要があります。
gptexmat
やgpcolormat
でマテリアルオブジェクトを作った場合、マテリアルの雛形として使用されます。直接ノードオブジェクトで使用されることはありません。
gpbox
やgpplate
などノードオブジェクト生成時に、先に作成しておいたマテリアルオブジェクトを指定すると、作成したオブジェクト内に指定したマテリアルと同じ内容のマテリアルオブジェクトが新たに生成されます。
オブジェクト生成前に作成したマテリアルは、ノードオブジェクトに紐づいていません。
このため、オブジェクト生成後に設定を変えてもノードオブジェクトには反映されません。
一方でgpnodeinfo
では、ノードオブジェクトに紐づいたマテリアルIDを取得するため、設定を変更するとノードオブジェクトに結果が反映されます。
ということでUnityのMaterial Variantみたいな一括変更は、HGIMG4ではできません。出来たら出来たで余計わかりにくい気がするので、今の仕様はHSP3的でわかりやすくていいですね。 また、オブジェクト生成前にマテリアルを作成する方法だと、マテリアルの設定変更が反映されない場合があるのでこの点にも注意です。(HSP3.7β4現在)
言葉だけではわかりにくいのでサンプルです。
#include "hgimg4.as"
;-----------------------------
; 環境
;-----------------------------
gpreset
setcls CLSMODE_SOLID, $808080 ; 画面クリア設定
setpos GPOBJ_CAMERA, 0, 2, 4 ; カメラ位置を設定
gplookat GPOBJ_CAMERA, 0,0.5,0
;-----------------------------
; マテリアルを生成
;-----------------------------
gptexmat id_mat0, "res/qbox.png"
; 裏面のみ表示(表面をカリング)
gpmatstate id_mat0, "cullFaceSide", "FRONT"
;-----------------------------
; 箱ノード作成
;-----------------------------
; 同じ箱を2つ作成
gpbox id_box1, 1, -1, id_mat0
gpbox id_box2, 1, -1, id_mat0
setpos id_box1, -1.0, 0.5, 0.0
setpos id_box2, 1.0, 0.5, 0.0
gpsetprm id_box1, PRMSET_ALPHA, 127
gpsetprm id_box2, PRMSET_ALPHA, 127
;-----------------------------
; マテリアルの設定を変更
;-----------------------------
; 右の箱(id_box1)のマテリアルを変更します。
; 箱が実際に使用しているマテリアルIDを取得して変更します。
; 生成されたマテリアルIDを取得
; IDを確認するために取得しています。設定変更には使用していません。
gpnodeinfo id_mat1, id_box1, GPNODEINFO_MATERIAL
; 生成されたマテリアルIDを取得
gpnodeinfo id_mat2, id_box2, GPNODEINFO_MATERIAL
; Zバッファ書き込み無効
gpmatstate id_mat2, "cullFace", "false"
gpmatstate id_mat2, "depthWrite", "false"
;gpmatprmt id_mat2, , "res/body_SD.png"
; id_mat0はノードオブジェクトには使用されていないので、
; 変更してもオブジェクトには反映されません。
;gpmatstate id_mat0, "cullFace", "false"
;gpmatstate id_mat0, "depthWrite", "false"
; gpcloneだとマテリアルが取得できない
;gpclone id_box3, id_box1
;setpos id_box3, 0.0, 0.5, 0.0
;; gpsetprm id_box3, PRMSET_USEGPMAT, id_mat0 ; 設定しても反映されない。
;gpnodeinfo id_mat3, id_box3, GPNODEINFO_MATERIAL
;logmes "" + id_mat3 ; -1(マテリアルなし)になっている。
;gpmatstate id_mat3, "cullFace", "false"
;gpmatstate id_mat3, "depthWrite", "false"
;-----------------------------
; 床ノード作成
;-----------------------------
gpfloor id_floor, 30,30, $404040
*main
stick key,15
if key&128 : end
redraw 0
gpdraw
pos 10, 10
; マテリアルIDの違い
; オブジェクト生成に使用したマテリアル
mes "id_mat0 = " + id_mat0
; 生成されたマテリアル
mes "id_mat1 = " + id_mat1
mes "id_mat2 = " + id_mat2
redraw 1
await 1000/60
goto *main
左右どちらの箱も半透明表示です。左側の箱は、表面を非表示にして裏面だけ表示するようにしています。 右側の箱は、裏表どちらの面の表示しつつ深度での比較を行わないようにして、重なっている部分もすべて描画するようにしています。 ウィンドウ左上にマテリアルIDを表示しています。
オブジェクトが作成されると、マテリアルIDが生成されたことを確認できると思います。
また、gpmatstate
をコメントにすると共通のマテリアルから生成されたことが確認できます。
そういえばgpmatprmt
に触れてませんでしたが、テクスチャ画像を変更する場合に使用します。
コメントにしている行を使うと、オブジェクト生成前に作成したマテリアルid_mat0
の設定を変更しても後から作った箱には反映されないことが確認できます。
また、gpclone
でオブジェクトを作成するとgpnodeinfo
でマテリアルが取得できないようです。これは不具合なんじゃないだろうか。(HSP3.7β4)
depthFunc
あまり変更する機会はないと思いますが、depthFunc
(Z値比較方法)についてちょっと書いてみます。
通常は不透明なノードオブジェクトを描画する際、これから描くものが手前のもので隠れている位置の場合は、見えないはずなので描画しません。 これは「すでに描画されているもの(DST = DESTINATION)」と「これから描画するもの(SRC = SOURCE)」それぞれのカメラからの距離(Zバッファ、深度)を比較することで判定を行っています。
この判定方法だと半透明なオブジェクトの場合、隠れているオブジェクトは見えないはずという前提は成り立たたないため、正しく描画されません。 また、カメラから遠い方のオブジェクトを描画したいといった特殊な表現にも対応できません。
そこでdepthFunc
(Z値比較方法)を使うと、この深度テストの評価方法を変更できます。
評価方法を逆にしてカメラから遠い方を描画したり、無効にして距離に関係なく描画したりといった事が可能になります。
実際に動きを見たほうがわかりやすいので、サンプルを書いてみました。
;depthFuncのサンプル
;↑↓スペースで壁を動かせます。
;←→でdepthFuncを変更できます。
;
#include "hgimg4.as"
;-----------------------------
; 環境
;-----------------------------
gpreset
setcls CLSMODE_SOLID, $808080 ; 画面クリア設定
setpos GPOBJ_CAMERA, 0, 2, 6 ; カメラ位置を設定
gplookat GPOBJ_CAMERA, 0,0.5,0
;-----------------------------
; マテリアルを生成
;-----------------------------
;gptexmat id_matbase, "res/qbox.png" ; テクスチャあり
gpcolormat id_matbase, 0xff00ff ; テクスチャなし
; 深度テストの評価方法
; 深度(Z値、カメラからの距離)の比較方法を指定します。
; SRC = SOURCE これから描こうとしているもの
; DST = DESTINATION すでに描かれているもの
sdim depthFunc, 64, 8
depthFunc(0) = "NEVER" ; FALSE →描画しない
depthFunc(1) = "LESS" ; SRC < DST →近い方を描画(デフォルト値)
depthFunc(2) = "EQUL" ; SRC = DST →重なっている部分だけを描画
depthFunc(3) = "LEQUAL" ; SRC <= DST →近い方を描画
depthFunc(4) = "GREATER" ; SRC > DST →遠くにある方を描画
depthFunc(5) = "NOTEQUAL" ; SRC != DST →重なっていない部分を描画
depthFunc(6) = "GEQUAL" ; SRC >= DST →遠くにある方を描画
depthFunc(7) = "ALWAYS" ; TRUE →全て描画
mid = 1
mid0 = mid
gpmatstate id_matbase, "depthFunc", depthFunc(mid)
;-----------------------------
; 箱ノード量産
;-----------------------------
dim id_boxc, 6
dim id_mat, 6
repeat 6
;gpbox id_boxc(cnt), 1, -1
;gpsetprm id_boxc(cnt), PRMSET_USEGPMAT, id_matbase
gpbox id_boxc(cnt), 1, -1, id_matbase
gpnodeinfo id_mat(cnt), id_boxc(cnt), GPNODEINFO_MATERIAL
loop
; 配置
; 左の3つ
setpos id_boxc(0), -2.0, 0.5, 0.0
setpos id_boxc(1), -1.5, 0.5, -1.0
setpos id_boxc(2), -1.0, 0.5, -2.0
; 右の3つ
setpos id_boxc(3), 1.0, 0.5, -2.0
setpos id_boxc(4), 1.5, 0.5, -1.0
setpos id_boxc(5), 2.0, 0.5, 0.0
; 半透明
repeat 6
gpsetprm id_boxc(cnt), PRMSET_ALPHA, 127
; gpsetprm id_boxc(cnt), PRMSET_MODE, OBJ_LATE ; 後から描画
loop
;-----------------------------
; 床ノード
;-----------------------------
gpfloor id_floor, 30,30, $404040
;-----------------------------
; 壁
;-----------------------------
gpplate id_plate, 6, 3, $303050
setpos id_plate, 0, 0, -0.5
*main
stick key, 2+8
if key&128 : end
redraw 0
; 壁を移動
;↑↓スペース
getpos id_plate, x,y,z
if key & 2 : z -= 0.1
if key & 8 : z += 0.1
if key & 16 : z = -0.5
setpos id_plate, x,y,z
; depthFuncを変更
;←→
if key & 1 : mid--
if key & 4 : mid++
if mid > 7 : mid = 0
if mid < 0 : mid = 7
if mid ! mid0 {
repeat 6
gpmatstate id_mat(cnt), "depthFunc", depthFunc(mid)
loop
}
mid0 = mid
gpdraw
pos 10,10
mes "板を移動 : ↑、↓、スペース"
mes "マテリアルの設定を変更 : ←→\n"
mes "depthFunc = " + depthFunc(mid)
if depthFunc(mid) = "NEVER" : s = "FALSE 描画しない"
if depthFunc(mid) = "LESS" : s = "SRC < DST 近い方を描画(デフォルト値)"
if depthFunc(mid) = "EQUL" : s = "SRC = DST 重なっている部分だけを描画"
if depthFunc(mid) = "LEQUAL" : s = "SRC <= DST 近い方を描画"
if depthFunc(mid) = "GREATER" : s = "SRC > DST 遠くにある方を描画"
if depthFunc(mid) = "NOTEQUAL" : s = "SRC != DST 重なっていない部分を描画"
if depthFunc(mid) = "GEQUAL" : s = "SRC >= DST 遠くにある方を描画"
if depthFunc(mid) = "ALWAYS" : s = "TRUE 全て描画"
mes s
redraw 1
await 1000/60
goto *main
「↑、↓、スペース」キーで板を動かすことができます。
「←、→」でdepthFunc
を変更できます。
箱は、左の箱から右への順で作成しました。
箱が重なっている部分や板と箱の関係に注視してください。
HGIMG4の場合、LESS
が初期値のようです。
GREATER
にすると板よりも遠い箱だけが表示されるので、板の中にしか箱が見えない状態になっていておもしろい表現です。
ALWAYS
にするとどの箱も、箱どうしが重なっている部分がかける事なく描画されています。
EQUL
はLESS
と同じになっているのは…いいのかこれ?
gpsetprm
あまりオススメはしませんが、gpbox
でオブジェクトを作成した後にマテリアルを適用することもできます。gpsetprm
を使用します。
さきほどのサンプルのgpbox
で箱を作成している部分を次のように書き換えます。
repeat 6
gpbox id_boxc(cnt), 1, -1
gpsetprm id_boxc(cnt), PRMSET_USEGPMAT, id_matbase
loop
これだけなのですが、HSP3.7β4現在では不具合があるのか半透明描画ができません。
箱の色が変わったりはするので、この書き方でマテリアルは適用されているはずなんですけどね。
HSP3.7β5で半透明表示されるように修正されました。
また、複数のマテリアルを内包している3Dモデル(gpbファイルから生成された3Dモデル)は、この方法で書き換えを行うことができないので注意してください。
3Dモデルのマテリアル設定変更
複数のマテリアルを内包している3Dモデル(gpbファイルから生成された3Dモデル)のマテリアルの設定変更をやってみます。 HSP3に付属のtamane2.gpbがちょうど良さそうなので、これを使ってやってみます。
しかしtamane2.gpbのような3Dモデルの場合、ここまでの説明の方法だけでは設定変更できません。
ここまでは、ノードオブジェクト1つに付き1つのマテリアルがある前提でしたが、tamane2.gpbの場合は4つのマテリアルを内包しています。
tamane2.gpbは、構造がatama
、ude
、body
、kosi
に分かれており、それぞれ別々のマテリアルオブジェクトを持っています。
マテリアルの設定変更をするには、内包しているマテリアルIDを取得して1個ずつ変更する必要があります。
マテリアルIDを取得するには、gpnodeinfo
に「階層ノード名」を追加するだけです。
この後の操作は、これまで取り扱っていた方法と同じです。前置きの割に簡単でしたね。
サンプルを書いてみました。
;
; 3Dモデルのマテリアル設定変更
;
#include "hgimg4.as"
;------------------------------
; 環境
;------------------------------
gpreset
setcls CLSMODE_SOLID, $404040
setcolor GPOBJ_LIGHT, 1,1,1
setdir GPOBJ_LIGHT, 0.5,0.5,0.5
;------------------------------
; オブジェクトノード
;------------------------------
; 床
gpfloor id_floor, 30,30, $4040F0
; 壁
gpplate id_plate, 10, 60, $303050
setpos id_plate, 5, 0, 10
isPlateHide = 0 ; 壁表示
; 3Dモデル
gpload id_model,"res/tamane2" ; モデル読み込み
setscale id_model, 0.1,0.1,0.1
; 半透明
gpsetprm id_model, PRMSET_ALPHA, 127
; アニメーションクリップ
gpact id_model
;------------------------------
; マテリアルを変更
;------------------------------
; マテリアルIDを取得
; 3Dモデルが複数の階層構造を持つ場合は、各ノード毎にマテリアルIDを
; 取得する必要があります。
gpnodeinfo mat_id(0), id_model, GPNODEINFO_MATERIAL, "atama"
gpnodeinfo mat_id(1), id_model, GPNODEINFO_MATERIAL, "ude"
gpnodeinfo mat_id(2), id_model, GPNODEINFO_MATERIAL, "body"
gpnodeinfo mat_id(3), id_model, GPNODEINFO_MATERIAL, "kosi"
repeat 4
logmes "" + mat_id(cnt)
loop
; 階層構造を考えずに取得すると-1が返されます。
gpnodeinfo mid, id_model, GPNODEINFO_MATERIAL
logmes "-> "+mid
;------------------------------
; カメラ
;------------------------------
setpos GPOBJ_CAMERA, 0,20,30
;------------------------------
; メインループ
;------------------------------
*main
stick key, 256
if key&128 : end
redraw 0 ; 描画開始
;------------------------------
; マテリアルの設定を変更
;------------------------------
;←→
if key & 1 : mode--
if key & 4 : mode++
if mode > 5 : mode = 0
if mode < 0 : mode = 5
if mode ! mode0 {
; 複数の階層構造を持つ3Dモデル全体に対してのマテリアル設定変更はできません。
; 全ての階層構造の一つ一つ全てに、マテリアルを設定する必要があります。
; blend ブレンド無効 → 不透明な描画になるはず
; cullFace カリング無効 → 裏面も見えるはず
; depthTest 深度テスト無効 → 奥側にある面も全て描画されるはず
; depthWrite 深度書き込み無効 → 奥側にある面も全て描画されるはず
blend = "true"
cullFace = "true"
depthTest = "true"
depthWrite = "true"
if mode = 0 : blend = "false"
if mode = 1 : cullFace = "false"
if mode = 2 : depthTest = "false"
if mode = 3 : depthWrite = "false"
if mode = 4 : cullFace = "false" : depthWrite = "false"
repeat 4
gpmatstate mat_id(cnt), "blend", blend
gpmatstate mat_id(cnt), "cullFace", cullFace
gpmatstate mat_id(cnt), "depthTest", depthTest
gpmatstate mat_id(cnt), "depthWrite", depthWrite
loop
}
mode0 = mode
;------------------------------
; 壁の表示・非表示
;------------------------------
if key & 16 {
isPlateHide ^= 1
if isPlateHide {
setobjmode id_plate, OBJ_HIDE, 0
} else {
setobjmode id_plate, OBJ_HIDE, 1
}
}
;------------------------------
; ドラッグ上下でカメラ操作
;------------------------------
if key & 256 {
dy=0.05*(mousey-dragy)+cy
setpos GPOBJ_CAMERA, dx,dy,cz
} else {
dragy=mousey
getpos GPOBJ_CAMERA, cx,cy,cz
}
gplookat GPOBJ_CAMERA, 0,14,0
addang id_model,0,0.02 ; ノード回転
gpdraw ; シーンの描画
;------------------------------
; 状態を表示
;------------------------------
color 255,255,255
pos 8,8
mes "マテリアルID"
mes "atama:" + mat_id(0)
mes "ude :" + mat_id(1)
mes "body :" + mat_id(2)
mes "kosi :" + mat_id(3)
mes "指定なし:"+mid
mes ""
; マテリアルの設定
mes "マテリアルの設定(←→で変更)"
s = ""
s += "blend : " : if blend = "true" : s+="○" : else : s+="×"
s += "\ncullFace : " : if cullFace = "true" : s+="○" : else : s+="×"
s += "\ndepthTest : " : if depthTest = "true" : s+="○" : else : s+="×"
s += "\ndepthWrite : " : if depthWrite = "true" : s+="○" : else : s+="×"
mes s
mes "\nスペースキー:壁の表示/非表示"
mes "マウス上下ドラッグ:カメラ移動"
redraw 1 ; 描画終了
await 1000/60 ; 待ち時間
goto *main
tamane2.gpbを半透明表示しています。 「←、→、スペース、マウスドラッグ」で操作できます。右半分の板は「スペースキー」で表示非表示の切り替えができます。
構造毎にマテリアルIDが別々になっていることがわかります。また、階層ノード名を指定しない場合は、-1(マテリアルなし)になっています。
マテリアルの設定を「←、→」キーで切り替えると、その効果の違いがわかります。
やはりdepthWrite
は複雑な3Dモデルを半透明表示する際にこそ意味のある設定項目ですね。true
にしておかないと、見えないはずのものが見えてしまって見栄えがよくありません。
tamane2.materialは、マテリアルの設定が適切に行われていることもわかりましたね。
blend / cullFace / depthTest / depthWrite
- / cullFace / depthTest / depthWrite
blend / - / depthTest / depthWrite
アルファブレンディング
オブジェクトが重なった部分を半透明描画をする際、重なった部分は元の色と重ねた色を混ぜた色になります。 この色を混ぜることをブレンディングと言うそうです。また特に、透明色情報(アルファ値)を使って色を混ぜることをアルファブレンディングというようです。 重なった色の混ぜ合わせ方(α値を掛けたり、地の色を掛けたりなど)も計算方法が色々あるのですが、これはブレンドファクターと呼ばれています。 ってここを書くにあたって参考にしたサイトに書いてあったので、ここでもこの呼び名を使用します。
参考資料:
wgld.org アルファブレンディング
https://wgld.org/d/webgl/w029.html
hgimg4でもこの設定を変更できます。materialファイルを書き換えるかgpmatstate
命令を使うことで変更が可能です。
ブレンディングを有効にするには、blend
をtrue
に設定します。
ブレンドファクターの設定は、blendSrc
、blendDst
で設定を行います。
設定は単純なんですが、知識がないと意味がわからない部分です。
総当たりでいい感じの設定を探すのは大変なので、少し意味を理解したいと思います。
ブレンディングは、すでに描かれている色(描画先、DST = DESTINATION)の上に これから描こうとしている色(描画元、SRC = SOURCE)を乗せて混ぜ合わせる処理です。取り扱う色は、この2つだけです。 絵を描く方の場合は、ペイントソフトでレイヤーを重ねる操作をイメージするとわかりやすいかもしれません。 また、ブレンドファクターは、ペイントソフトのレイヤーのブレンドモード(乗算とか覆い焼きとか)をイメージするといいと思います。
雰囲気をつかんだところで、ブレンドファクターの一覧を見てみます。
定数名 | 内容 | 値・式(R, G, B, A) |
---|---|---|
ZERO | 即値(0) | (0, 0, 0, 0) |
ONE | 即値(1) | (1, 1, 1, 1) |
SRC_COLOR | 書き込み元カラー | (Rs, Gs, Bs, As) |
DST_COLOR | 書き込み先カラー | (Rd, Gd, Bd, Ad) |
ONE_MINUS_SRC_COLOR | 書き込み元カラー(反転値) | (1, 1, 1, 1) - (Rs, Gs, Bs, As) |
ONE_MINUS_DST_COLOR | 書き込み先カラー(反転値) | (1, 1, 1, 1) - (Rd, Gd, Bd, Ad) |
SRC_ALPHA | 書き込み元α | (As, As, As, As) |
DST_ALPHA | 書き込み先α | (Ad, Ad, Ad, Ad) |
ONE_MINUS_SRC_ALPHA | 書き込み元α(反転値) | (1, 1, 1, 1) - (As, As, As, As) |
ONE_MINUS_DST_ALPHA | 書き込み先α(反転値) | (1, 1, 1, 1) - (Ad, Ad, Ad, Ad) |
CONSTANT_COLOR | (hgimg4未実装) | (Rc, Gc, Bc, Ac) |
ONE_MINUS_CONSTANT_COLOR | (hgimg4未実装) | (1, 1, 1, 1) - (Rc, Gc, Bc, Ac) |
CONSTANT_ALPHA | α固定値 | (Ac, Ac, Ac, Ac) |
ONE_MINUS_CONSTANT_ALPHA | α固定値(反転値) | (1, 1, 1, 1) - (Ac, Ac, Ac, Ac) |
SRC_ALPHA_SATURATE | 書き込み元α反転値 | (f, f, f, 1) f = min(As, 1 - Ad) |
どこがブレンドモードなんだか。意味わからないですね。まずは、表の見方から。定数名の意味は以下の表のようになっています。
例えば、ONE_MINUS_SRC_ALPHA
なら「1 - 描画元のアルファ値」という感じです。そのままですね。
定数名 | 概要 |
---|---|
ZERO | 0 |
ONE | 1 |
SRC | SOURCE 描画元、これから描こうとしている色 |
DST | DESTINATION 描画先、すでに描かれている色 |
_ALPHA | アルファ値 |
_COLOR | 色(R,G,B,A |
ONE_MINUS_○○ | 1 - ○○ |
CONSTANT | 定数 |
値・式のカッコ内は、(R,G,B,A)の4要素です。RGBAの右下の接尾辞は、以下の表の通り。 Rsなら描画元の赤ですね。
接尾辞 | 名前 | 意味 |
---|---|---|
s | source | 描画元 |
d | destination | 描画先 |
c | constant | 定数 |
選択した定数名の値・式を使って描画元と描画先の2色を計算して、描画色が決定されます。 例を用いて計算方法を説明してみます。
ブレンドファクターは、blendSrc、blendDstに定数名を指定することで設定します。 例えば、次のような設定を行った場合の描画色を考えてみます。単純な重ね合わせの場合です。
blendSrc = ONE
blendDst = ZERO
描画色 = 描画元の色 * blendSrc + 描画先の色 * blendDst
= (Rs,Gs,Bs,As) * (1,1,1,1) + (Rd,Gd,Bd,Ad) * (0,0,0,0)
= (Rs,Gs,Bs,As)
この設定の場合は、描画元の色で描画されます。もう1ケースやってみます。アルファブレンドの場合です。
blendSrc = SRC_ALPHA
blendDst = ONE_MINUS_SRC_ALPHA
描画色 = 描画元の色 * blendSrc + 描画先の色 * blendDst
= (Rs,Gs,Bs,As) * (As,As,As,As) + (Rd,Gd,Bd,Ad) * ((1, 1, 1, 1) - (As, As, As, As))
= (Rs*As, Gs*As, Bs*As, As*As) + (Rd*(1-As), Gd*(1-As), Bd*(1-As), Ad*(1-As))
= (Rs*As+Rd*(1-As), Gs*As+Gd*(1-As), Bs*As+Bd*(1-As), As*As+Ad*(1-As) )
数値を用いた具体例
描画元の色 (1 , 0, 0 , 0.7)
描画先の色 (0 , 0, 1 , 1)
描画色 (0.7, 0, 0.3, 0.79)
このように、値・式を掛け算して、その結果を足し合わせたものが描画色です。
なお、合計が1を超えたら1、0を下回ったら0になるようです。
値・式を足したとき、1になるような組み合わせであれば、黒っぽくなったり明るくなりすぎたりは無いと思います。
基本的にHOGEHOGE
とONE_MINUS_HOGEHOGE
みたいな組み合わせで使うと良さそうですね。
さて、以上のようにブレンドファクターは計算式を与えるのですが、計算式には計算式以上の意味がないので、このままでは使いにくい。 そこで、ペイントソフト等で見慣れたレイヤーのブレンドモードに相当する組み合わせを調べてみました。
参考資料:
パーティクルで学ぶ OpenGL ブレンディング ( Android OpenGL フレームワーク “Rajawali” と戯れる #06 ) https://dev.classmethod.jp/articles/android-rajawali-tutorials-06/
melpon日記 - HaskellもC++もまともに扱えないへたれのページ https://melpon.hatenadiary.org/entry/20070824/p1
blendSrc | blendDst | 名称 | アルファ値 |
---|---|---|---|
ONE | ZERO | 上書き | なし |
SRC_ALPHA | ONE_MINUS_SRC_ALPHA | アルファ | |
ONE | ONE | 加算(覆い焼きリニア) | なし |
SRC_ALPHA | ONE | 加算(覆い焼きリニア) | |
ZERO | SRC_COLOR | 乗算 | なし |
DST_COLOR | ZERO | 乗算 | なし |
ONE_MINUS_DST_COLOR | ONE | スクリーン | なし |
ONE | ONE_MINUS_SRC_COLOR | スクリーン | なし |
ONE_MINUS_DST_COLOR | ONE_MINUS_SRC_COLOR | XOR(排他的論理和) | なし |
ONE_MINUS_DST_COLOR | ZERO | 反転 | なし |
多少は見慣れた名前が付くので、使いやすくなった気がします。 使用していないブレンドファクターもいくつかありますが、調べてもどういうときに使うのかわかりませんでした。 使ってみて、描画結果がいい感じならそれでいいんだと思います。
blendSrc = SRC_ALPHA_SATURATE
blendDst = ONE
OpenGL Programming Guide
アンチエイリアシングが何だとか…やっぱりわかりません。
まとめ
マテリアルの設定変更には、materialファイルを書き換える方法とgpmatstate
を使用する方法があります。
プログラム側でマテリアルの設定を変更する場合は、いくつかのポイントを押さえないと設定が反映されないので注意が必要です。
基本的には、作成済みノードからgpnodeinfo
でマテリアルIDを取得して、gpmatstate
で変更という流れがいいようです。
細かい調整方法については、UnityとかWebGLあたりで調べると情報が出てくると思います。多分。