phaser4-rex-plugins
Version:
1,533 lines (1,351 loc) • 107 kB
text/typescript
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { Constant } from '../live2dcubismframework';
import { CubismMatrix44 } from '../math/cubismmatrix44';
import { CubismModel } from '../model/cubismmodel';
import { csmMap } from '../type/csmmap';
import { csmRect } from '../type/csmrectf';
import { csmVector } from '../type/csmvector';
import { CubismLogError, CubismLogWarning } from '../utils/cubismdebug';
import {
CubismBlendMode,
CubismRenderer,
CubismTextureColor,
} from './cubismrenderer';
const ColorChannelCount = 4; // 実験時に1チャンネルの場合は1、RGBだけの場合は3、アルファも含める場合は4
const ClippingMaskMaxCountOnDefault = 36; // 通常のフレームバッファ一枚あたりのマスク最大数
const ClippingMaskMaxCountOnMultiRenderTexture = 32; // フレームバッファが2枚以上ある場合のフレームバッファ一枚あたりのマスク最大数
const ShaderCount = 10; // シェーダーの数 = マスク生成用 + (通常用 + 加算 + 乗算) * (マスク無の乗算済アルファ対応版 + マスク有の乗算済アルファ対応版 + マスク有反転の乗算済アルファ対応版)
let s_instance: CubismShader_WebGL;
let s_viewport: number[];
let s_fbo: WebGLFramebuffer;
/**
* クリッピングマスクの処理を実行するクラス
*/
export class CubismClippingManager_WebGL {
/**
* カラーチャンネル(RGBA)のフラグを取得する
* @param channelNo カラーチャンネル(RGBA)の番号(0:R, 1:G, 2:B, 3:A)
*/
public getChannelFlagAsColor(channelNo: number): CubismTextureColor {
return this._channelColors.at(channelNo);
}
/**
* テンポラリのレンダーテクスチャのアドレスを取得する
* FrameBufferObjectが存在しない場合、新しく生成する
*
* @return レンダーテクスチャの配列
*/
public getMaskRenderTexture(): csmVector<WebGLFramebuffer> {
// テンポラリのRenderTextureを取得する
if (this._maskTexture && this._maskTexture.textures != null) {
// 前回使ったものを返す
this._maskTexture.frameNo = this._currentFrameNo;
} else {
// FrameBufferObjectが存在しない場合、新しく生成する
if (this._maskRenderTextures != null) {
this._maskRenderTextures.clear();
}
this._maskRenderTextures = new csmVector<WebGLFramebuffer>();
// ColorBufferObjectが存在しない場合、新しく生成する
if (this._maskColorBuffers != null) {
this._maskColorBuffers.clear();
}
this._maskColorBuffers = new csmVector<WebGLTexture>();
// クリッピングバッファサイズを取得
const size: number = this._clippingMaskBufferSize;
for (let index = 0; index < this._renderTextureCount; index++) {
this._maskColorBuffers.pushBack(this.gl.createTexture()); // 直接代入
this.gl.bindTexture(
this.gl.TEXTURE_2D,
this._maskColorBuffers.at(index)
);
this.gl.texImage2D(
this.gl.TEXTURE_2D,
0,
this.gl.RGBA,
size,
size,
0,
this.gl.RGBA,
this.gl.UNSIGNED_BYTE,
null
);
this.gl.texParameteri(
this.gl.TEXTURE_2D,
this.gl.TEXTURE_WRAP_S,
this.gl.CLAMP_TO_EDGE
);
this.gl.texParameteri(
this.gl.TEXTURE_2D,
this.gl.TEXTURE_WRAP_T,
this.gl.CLAMP_TO_EDGE
);
this.gl.texParameteri(
this.gl.TEXTURE_2D,
this.gl.TEXTURE_MIN_FILTER,
this.gl.LINEAR
);
this.gl.texParameteri(
this.gl.TEXTURE_2D,
this.gl.TEXTURE_MAG_FILTER,
this.gl.LINEAR
);
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
this._maskRenderTextures.pushBack(this.gl.createFramebuffer());
this.gl.bindFramebuffer(
this.gl.FRAMEBUFFER,
this._maskRenderTextures.at(index)
);
this.gl.framebufferTexture2D(
this.gl.FRAMEBUFFER,
this.gl.COLOR_ATTACHMENT0,
this.gl.TEXTURE_2D,
this._maskColorBuffers.at(index),
0
);
}
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, s_fbo);
this._maskTexture = new CubismRenderTextureResource(
this._currentFrameNo,
this._maskRenderTextures
);
}
return this._maskTexture.textures;
}
/**
* WebGLレンダリングコンテキストを設定する
* @param gl WebGLレンダリングコンテキスト
*/
public setGL(gl: WebGLRenderingContext): void {
this.gl = gl;
}
/**
* マスクされる描画オブジェクト群全体を囲む矩形(モデル座標系)を計算する
* @param model モデルのインスタンス
* @param clippingContext クリッピングマスクのコンテキスト
*/
public calcClippedDrawTotalBounds(
model: CubismModel,
clippingContext: CubismClippingContext
): void {
// 被クリッピングマスク(マスクされる描画オブジェクト)の全体の矩形
let clippedDrawTotalMinX: number = Number.MAX_VALUE;
let clippedDrawTotalMinY: number = Number.MAX_VALUE;
let clippedDrawTotalMaxX: number = Number.MIN_VALUE;
let clippedDrawTotalMaxY: number = Number.MIN_VALUE;
// このマスクが実際に必要か判定する
// このクリッピングを利用する「描画オブジェクト」がひとつでも使用可能であればマスクを生成する必要がある
const clippedDrawCount: number =
clippingContext._clippedDrawableIndexList.length;
for (
let clippedDrawableIndex = 0;
clippedDrawableIndex < clippedDrawCount;
clippedDrawableIndex++
) {
// マスクを使用する描画オブジェクトの描画される矩形を求める
const drawableIndex: number =
clippingContext._clippedDrawableIndexList[clippedDrawableIndex];
const drawableVertexCount: number =
model.getDrawableVertexCount(drawableIndex);
const drawableVertexes: Float32Array =
model.getDrawableVertices(drawableIndex);
let minX: number = Number.MAX_VALUE;
let minY: number = Number.MAX_VALUE;
let maxX: number = -Number.MAX_VALUE;
let maxY: number = -Number.MAX_VALUE;
const loop: number = drawableVertexCount * Constant.vertexStep;
for (
let pi: number = Constant.vertexOffset;
pi < loop;
pi += Constant.vertexStep
) {
const x: number = drawableVertexes[pi];
const y: number = drawableVertexes[pi + 1];
if (x < minX) {
minX = x;
}
if (x > maxX) {
maxX = x;
}
if (y < minY) {
minY = y;
}
if (y > maxY) {
maxY = y;
}
}
// 有効な点が一つも取れなかったのでスキップ
if (minX == Number.MAX_VALUE) {
continue;
}
// 全体の矩形に反映
if (minX < clippedDrawTotalMinX) {
clippedDrawTotalMinX = minX;
}
if (minY < clippedDrawTotalMinY) {
clippedDrawTotalMinY = minY;
}
if (maxX > clippedDrawTotalMaxX) {
clippedDrawTotalMaxX = maxX;
}
if (maxY > clippedDrawTotalMaxY) {
clippedDrawTotalMaxY = maxY;
}
if (clippedDrawTotalMinX == Number.MAX_VALUE) {
clippingContext._allClippedDrawRect.x = 0.0;
clippingContext._allClippedDrawRect.y = 0.0;
clippingContext._allClippedDrawRect.width = 0.0;
clippingContext._allClippedDrawRect.height = 0.0;
clippingContext._isUsing = false;
} else {
clippingContext._isUsing = true;
const w: number = clippedDrawTotalMaxX - clippedDrawTotalMinX;
const h: number = clippedDrawTotalMaxY - clippedDrawTotalMinY;
clippingContext._allClippedDrawRect.x = clippedDrawTotalMinX;
clippingContext._allClippedDrawRect.y = clippedDrawTotalMinY;
clippingContext._allClippedDrawRect.width = w;
clippingContext._allClippedDrawRect.height = h;
}
}
}
/**
* コンストラクタ
*/
public constructor() {
this._currentMaskRenderTexture = null;
this._maskColorBuffers = null;
this._currentFrameNo = 0;
this._renderTextureCount = 0;
this._clippingMaskBufferSize = 256;
this._clippingContextListForMask = new csmVector<CubismClippingContext>();
this._clippingContextListForDraw = new csmVector<CubismClippingContext>();
this._channelColors = new csmVector<CubismTextureColor>();
this._tmpBoundsOnModel = new csmRect();
this._tmpMatrix = new CubismMatrix44();
this._tmpMatrixForMask = new CubismMatrix44();
this._tmpMatrixForDraw = new CubismMatrix44();
this._maskTexture = null;
let tmp: CubismTextureColor = new CubismTextureColor();
tmp.R = 1.0;
tmp.G = 0.0;
tmp.B = 0.0;
tmp.A = 0.0;
this._channelColors.pushBack(tmp);
tmp = new CubismTextureColor();
tmp.R = 0.0;
tmp.G = 1.0;
tmp.B = 0.0;
tmp.A = 0.0;
this._channelColors.pushBack(tmp);
tmp = new CubismTextureColor();
tmp.R = 0.0;
tmp.G = 0.0;
tmp.B = 1.0;
tmp.A = 0.0;
this._channelColors.pushBack(tmp);
tmp = new CubismTextureColor();
tmp.R = 0.0;
tmp.G = 0.0;
tmp.B = 0.0;
tmp.A = 1.0;
this._channelColors.pushBack(tmp);
}
/**
* デストラクタ相当の処理
*/
public release(): void {
for (let i = 0; i < this._clippingContextListForMask.getSize(); i++) {
if (this._clippingContextListForMask.at(i)) {
this._clippingContextListForMask.at(i).release();
this._clippingContextListForMask.set(i, void 0);
}
this._clippingContextListForMask.set(i, null);
}
this._clippingContextListForMask = null;
// _clippingContextListForDrawは_clippingContextListForMaskにあるインスタンスを指している。上記の処理により要素ごとのDELETEは不要。
for (let i = 0; i < this._clippingContextListForDraw.getSize(); i++) {
this._clippingContextListForDraw.set(i, null);
}
this._clippingContextListForDraw = null;
if (this._maskTexture) {
for (let i = 0; i < this._maskTexture.textures.getSize(); i++) {
this.gl.deleteFramebuffer(this._maskTexture.textures.at(i));
}
this._maskTexture.textures.clear();
this._maskTexture.textures = null;
this._maskTexture = null;
}
for (let i = 0; i < this._channelColors.getSize(); i++) {
this._channelColors.set(i, null);
}
this._channelColors = null;
// テクスチャ解放
if (this._maskColorBuffers != null) {
for (let index = 0; index < this._maskColorBuffers.getSize(); index++) {
this.gl.deleteTexture(this._maskColorBuffers.at(index));
}
this._maskColorBuffers.clear();
}
this._maskColorBuffers = null;
if (this._maskRenderTextures != null) {
this._maskRenderTextures.clear();
}
this._maskRenderTextures = null;
if (this._clearedFrameBufferflags != null) {
this._clearedFrameBufferflags.clear();
}
this._clearedFrameBufferflags = null;
}
/**
* マネージャの初期化処理
* クリッピングマスクを使う描画オブジェクトの登録を行う
* @param model モデルのインスタンス
* @param drawableCount 描画オブジェクトの数
* @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのインデックスのリスト
* @param drawableMaskCounts 描画オブジェクトをマスクする描画オブジェクトの数
* @param renderTextureCount バッファの生成数
*/
public initialize(
model: CubismModel,
drawableCount: number,
drawableMasks: Int32Array[],
drawableMaskCounts: Int32Array,
renderTextureCount: number
): void {
// レンダーテクスチャの合計枚数の設定
// 1以上の整数でない場合はそれぞれ警告を出す
if (renderTextureCount % 1 != 0) {
CubismLogWarning(
'The number of render textures must be specified as an integer. The decimal point is rounded down and corrected to an integer.'
);
// 小数点以下を除去
renderTextureCount = ~~renderTextureCount;
}
if (renderTextureCount < 1) {
CubismLogWarning(
'The number of render textures must be an integer greater than or equal to 1. Set the number of render textures to 1.'
);
}
// 負の値が使われている場合は強制的に1枚と設定する
this._renderTextureCount = renderTextureCount < 1 ? 1 : renderTextureCount;
this._clearedFrameBufferflags = new csmVector<boolean>(
this._renderTextureCount
);
// クリッピングマスクを使う描画オブジェクトをすべて登録する
// クリッピングマスクは、通常数個程度に限定して使うものとする
for (let i = 0; i < drawableCount; i++) {
if (drawableMaskCounts[i] <= 0) {
// クリッピングマスクが使用されていないアートメッシュ(多くの場合使用しない)
this._clippingContextListForDraw.pushBack(null);
continue;
}
// 既にあるClipContextと同じかチェックする
let clippingContext: CubismClippingContext = this.findSameClip(
drawableMasks[i],
drawableMaskCounts[i]
);
if (clippingContext == null) {
// 同一のマスクが存在していない場合は生成する
clippingContext = new CubismClippingContext(
this,
drawableMasks[i],
drawableMaskCounts[i]
);
this._clippingContextListForMask.pushBack(clippingContext);
}
clippingContext.addClippedDrawable(i);
this._clippingContextListForDraw.pushBack(clippingContext);
}
}
/**
* クリッピングコンテキストを作成する。モデル描画時に実行する。
* @param model モデルのインスタンス
* @param renderer レンダラのインスタンス
*/
public setupClippingContext(
model: CubismModel,
renderer: CubismRenderer_WebGL
): void {
this._currentFrameNo++;
// 全てのクリッピングを用意する
// 同じクリップ(複数の場合はまとめて一つのクリップ)を使う場合は1度だけ設定する
let usingClipCount = 0;
for (
let clipIndex = 0;
clipIndex < this._clippingContextListForMask.getSize();
clipIndex++
) {
// 1つのクリッピングマスクに関して
const cc: CubismClippingContext =
this._clippingContextListForMask.at(clipIndex);
// このクリップを利用する描画オブジェクト群全体を囲む矩形を計算
this.calcClippedDrawTotalBounds(model, cc);
if (cc._isUsing) {
usingClipCount++; // 使用中としてカウント
}
}
// マスク作成処理
if (usingClipCount > 0) {
// 各マスクのレイアウトを決定していく
this.setupLayoutBounds(
renderer.isUsingHighPrecisionMask() ? 0 : usingClipCount
);
if (!renderer.isUsingHighPrecisionMask()) {
// 生成したFrameBufferと同じサイズでビューポートを設定
this.gl.viewport(
0,
0,
this._clippingMaskBufferSize,
this._clippingMaskBufferSize
);
// 後の計算のためにインデックスの最初をセット
this._currentMaskRenderTexture = this.getMaskRenderTexture().at(0);
renderer.preDraw(); // バッファをクリアする
// ---------- マスク描画処理 ----------
// マスク用RenderTextureをactiveにセット
this.gl.bindFramebuffer(
this.gl.FRAMEBUFFER,
this._currentMaskRenderTexture
);
}
// サイズがレンダーテクスチャの枚数と合わない場合は合わせる
if (this._clearedFrameBufferflags.getSize() != this._renderTextureCount) {
this._clearedFrameBufferflags.clear();
this._clearedFrameBufferflags = new csmVector<boolean>(
this._renderTextureCount
);
}
// マスクのクリアフラグを毎フレーム開始時に初期化
for (
let index = 0;
index < this._clearedFrameBufferflags.getSize();
index++
) {
this._clearedFrameBufferflags.set(index, false);
}
// 実際にマスクを生成する
// 全てのマスクをどのようにレイアウトして描くかを決定し、ClipContext, ClippedDrawContextに記憶する
for (
let clipIndex = 0;
clipIndex < this._clippingContextListForMask.getSize();
clipIndex++
) {
// --- 実際に1つのマスクを描く ---
const clipContext: CubismClippingContext =
this._clippingContextListForMask.at(clipIndex);
const allClipedDrawRect: csmRect = clipContext._allClippedDrawRect; // このマスクを使う、すべての描画オブジェクトの論理座標上の囲み矩形
const layoutBoundsOnTex01: csmRect = clipContext._layoutBounds; // この中にマスクを収める
const MARGIN = 0.05; // モデル座標上の矩形を、適宜マージンを付けて使う
let scaleX = 0;
let scaleY = 0;
// clipContextに設定したレンダーテクスチャをインデックスで取得
const clipContextRenderTexture = this.getMaskRenderTexture().at(
clipContext._bufferIndex
);
// 現在のレンダーテクスチャがclipContextのものと異なる場合
if (
this._currentMaskRenderTexture != clipContextRenderTexture &&
!renderer.isUsingHighPrecisionMask()
) {
this._currentMaskRenderTexture = clipContextRenderTexture;
renderer.preDraw(); // バッファをクリアする
// マスク用RenderTextureをactiveにセット
this.gl.bindFramebuffer(
this.gl.FRAMEBUFFER,
this._currentMaskRenderTexture
);
}
if (renderer.isUsingHighPrecisionMask()) {
const ppu: number = model.getPixelsPerUnit();
const maskPixelSize: number =
clipContext.getClippingManager()._clippingMaskBufferSize;
const physicalMaskWidth: number =
layoutBoundsOnTex01.width * maskPixelSize;
const physicalMaskHeight: number =
layoutBoundsOnTex01.height * maskPixelSize;
this._tmpBoundsOnModel.setRect(allClipedDrawRect);
if (this._tmpBoundsOnModel.width * ppu > physicalMaskWidth) {
this._tmpBoundsOnModel.expand(
allClipedDrawRect.width * MARGIN,
0.0
);
scaleX = layoutBoundsOnTex01.width / this._tmpBoundsOnModel.width;
} else {
scaleX = ppu / physicalMaskWidth;
}
if (this._tmpBoundsOnModel.height * ppu > physicalMaskHeight) {
this._tmpBoundsOnModel.expand(
0.0,
allClipedDrawRect.height * MARGIN
);
scaleY = layoutBoundsOnTex01.height / this._tmpBoundsOnModel.height;
} else {
scaleY = ppu / physicalMaskHeight;
}
} else {
this._tmpBoundsOnModel.setRect(allClipedDrawRect);
this._tmpBoundsOnModel.expand(
allClipedDrawRect.width * MARGIN,
allClipedDrawRect.height * MARGIN
);
//########## 本来は割り当てられた領域の全体を使わず必要最低限のサイズがよい
// シェーダ用の計算式を求める。回転を考慮しない場合は以下のとおり
// movePeriod' = movePeriod * scaleX + offX [[ movePeriod' = (movePeriod - tmpBoundsOnModel.movePeriod)*scale + layoutBoundsOnTex01.movePeriod ]]
scaleX = layoutBoundsOnTex01.width / this._tmpBoundsOnModel.width;
scaleY = layoutBoundsOnTex01.height / this._tmpBoundsOnModel.height;
}
// マスク生成時に使う行列を求める
{
// シェーダに渡す行列を求める <<<<<<<<<<<<<<<<<<<<<<<< 要最適化(逆順に計算すればシンプルにできる)
this._tmpMatrix.loadIdentity();
{
// layout0..1 を -1..1に変換
this._tmpMatrix.translateRelative(-1.0, -1.0);
this._tmpMatrix.scaleRelative(2.0, 2.0);
}
{
// view to layout0..1
this._tmpMatrix.translateRelative(
layoutBoundsOnTex01.x,
layoutBoundsOnTex01.y
);
this._tmpMatrix.scaleRelative(scaleX, scaleY); // new = [translate][scale]
this._tmpMatrix.translateRelative(
-this._tmpBoundsOnModel.x,
-this._tmpBoundsOnModel.y
);
// new = [translate][scale][translate]
}
// tmpMatrixForMaskが計算結果
this._tmpMatrixForMask.setMatrix(this._tmpMatrix.getArray());
}
//--------- draw時の mask 参照用行列を計算
{
// シェーダに渡す行列を求める <<<<<<<<<<<<<<<<<<<<<<<< 要最適化(逆順に計算すればシンプルにできる)
this._tmpMatrix.loadIdentity();
{
this._tmpMatrix.translateRelative(
layoutBoundsOnTex01.x,
layoutBoundsOnTex01.y
);
this._tmpMatrix.scaleRelative(scaleX, scaleY); // new = [translate][scale]
this._tmpMatrix.translateRelative(
-this._tmpBoundsOnModel.x,
-this._tmpBoundsOnModel.y
);
// new = [translate][scale][translate]
}
this._tmpMatrixForDraw.setMatrix(this._tmpMatrix.getArray());
}
clipContext._matrixForMask.setMatrix(this._tmpMatrixForMask.getArray());
clipContext._matrixForDraw.setMatrix(this._tmpMatrixForDraw.getArray());
if (!renderer.isUsingHighPrecisionMask()) {
const clipDrawCount: number = clipContext._clippingIdCount;
for (let i = 0; i < clipDrawCount; i++) {
const clipDrawIndex: number = clipContext._clippingIdList[i];
// 頂点情報が更新されておらず、信頼性がない場合は描画をパスする
if (
!model.getDrawableDynamicFlagVertexPositionsDidChange(
clipDrawIndex
)
) {
continue;
}
renderer.setIsCulling(
model.getDrawableCulling(clipDrawIndex) != false
);
// マスクがクリアされていないなら処理する
if (!this._clearedFrameBufferflags.at(clipContext._bufferIndex)) {
// マスクをクリアする
// (仮仕様) 1が無効(描かれない)領域、0が有効(描かれる)領域。(シェーダーCd*Csで0に近い値をかけてマスクを作る。1をかけると何も起こらない)
this.gl.clearColor(1.0, 1.0, 1.0, 1.0);
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
this._clearedFrameBufferflags.set(clipContext._bufferIndex, true);
}
// 今回専用の変換を適用して描く
// チャンネルも切り替える必要がある(A,R,G,B)
renderer.setClippingContextBufferForMask(clipContext);
renderer.drawMesh(
model.getDrawableTextureIndex(clipDrawIndex),
model.getDrawableVertexIndexCount(clipDrawIndex),
model.getDrawableVertexCount(clipDrawIndex),
model.getDrawableVertexIndices(clipDrawIndex),
model.getDrawableVertices(clipDrawIndex),
model.getDrawableVertexUvs(clipDrawIndex),
model.getMultiplyColor(clipDrawIndex),
model.getScreenColor(clipDrawIndex),
model.getDrawableOpacity(clipDrawIndex),
CubismBlendMode.CubismBlendMode_Normal, // クリッピングは通常描画を強制
false // マスク生成時はクリッピングの反転使用は全く関係がない
);
}
}
}
if (!renderer.isUsingHighPrecisionMask()) {
// --- 後処理 ---
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, s_fbo); // 描画対象を戻す
renderer.setClippingContextBufferForMask(null);
this.gl.viewport(
s_viewport[0],
s_viewport[1],
s_viewport[2],
s_viewport[3]
);
}
}
}
/**
* 既にマスクを作っているかを確認
* 作っている様であれば該当するクリッピングマスクのインスタンスを返す
* 作っていなければNULLを返す
* @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのリスト
* @param drawableMaskCounts 描画オブジェクトをマスクする描画オブジェクトの数
* @return 該当するクリッピングマスクが存在すればインスタンスを返し、なければNULLを返す
*/
public findSameClip(
drawableMasks: Int32Array,
drawableMaskCounts: number
): CubismClippingContext {
// 作成済みClippingContextと一致するか確認
for (let i = 0; i < this._clippingContextListForMask.getSize(); i++) {
const clippingContext: CubismClippingContext =
this._clippingContextListForMask.at(i);
const count: number = clippingContext._clippingIdCount;
// 個数が違う場合は別物
if (count != drawableMaskCounts) {
continue;
}
let sameCount = 0;
// 同じIDを持つか確認。配列の数が同じなので、一致した個数が同じなら同じ物を持つとする
for (let j = 0; j < count; j++) {
const clipId: number = clippingContext._clippingIdList[j];
for (let k = 0; k < count; k++) {
if (drawableMasks[k] == clipId) {
sameCount++;
break;
}
}
}
if (sameCount == count) {
return clippingContext;
}
}
return null; // 見つからなかった
}
/**
* クリッピングコンテキストを配置するレイアウト
* 指定された数のレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトする
* マスクグループの数が4以下ならRGBA各チャンネルに一つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する。
*
* @param usingClipCount 配置するクリッピングコンテキストの数
*/
public setupLayoutBounds(usingClipCount: number): void {
const useClippingMaskMaxCount =
this._renderTextureCount <= 1
? ClippingMaskMaxCountOnDefault
: ClippingMaskMaxCountOnMultiRenderTexture * this._renderTextureCount;
if (usingClipCount <= 0 || usingClipCount > useClippingMaskMaxCount) {
if (usingClipCount > useClippingMaskMaxCount) {
// マスクの制限数の警告を出す
CubismLogError(
'not supported mask count : {0}\n[Details] render texture count : {1}, mask count : {2}',
usingClipCount - useClippingMaskMaxCount,
this._renderTextureCount,
usingClipCount
);
}
// この場合は一つのマスクターゲットを毎回クリアして使用する
for (
let index = 0;
index < this._clippingContextListForMask.getSize();
index++
) {
const clipContext: CubismClippingContext =
this._clippingContextListForMask.at(index);
clipContext._layoutChannelNo = 0; // どうせ毎回消すので固定
clipContext._layoutBounds.x = 0.0;
clipContext._layoutBounds.y = 0.0;
clipContext._layoutBounds.width = 1.0;
clipContext._layoutBounds.height = 1.0;
clipContext._bufferIndex = 0;
}
return;
}
// レンダーテクスチャが1枚なら9分割する(最大36枚)
const layoutCountMaxValue = this._renderTextureCount <= 1 ? 9 : 8;
// 指定された数のレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトする(デフォルトなら1)
// マスクグループの数が4以下ならRGBA各チャンネルに1つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する
let countPerSheetDiv: number = usingClipCount / this._renderTextureCount; // レンダーテクスチャ1枚あたり何枚割り当てるか
let countPerSheetMod: number = usingClipCount % this._renderTextureCount; // この番号のレンダーテクスチャまでに一つずつ配分する
// 小数点は切り捨てる
countPerSheetDiv = ~~countPerSheetDiv;
countPerSheetMod = ~~countPerSheetMod;
// RGBAを順番に使っていく
let div: number = countPerSheetDiv / ColorChannelCount; // 1チャンネルに配置する基本のマスク
let mod: number = countPerSheetDiv % ColorChannelCount; // 余り、この番号のチャンネルまでに一つずつ配分する
// 小数点は切り捨てる
div = ~~div;
mod = ~~mod;
// RGBAそれぞれのチャンネルを用意していく(0:R, 1:G, 2:B, 3:A)
let curClipIndex = 0; // 順番に設定していく
for (
let renderTextureNo = 0;
renderTextureNo < this._renderTextureCount;
renderTextureNo++
) {
for (let channelNo = 0; channelNo < ColorChannelCount; channelNo++) {
// このチャンネルにレイアウトする数
let layoutCount: number = div + (channelNo < mod ? 1 : 0);
// このレンダーテクスチャにまだ割り当てられていなければ追加する
const checkChannelNo = mod + 1 >= ColorChannelCount ? 0 : mod + 1;
if (layoutCount < layoutCountMaxValue && channelNo == checkChannelNo) {
layoutCount += renderTextureNo < countPerSheetMod ? 1 : 0;
}
// 分割方法を決定する
if (layoutCount == 0) {
// 何もしない
} else if (layoutCount == 1) {
// 全てをそのまま使う
const clipContext: CubismClippingContext =
this._clippingContextListForMask.at(curClipIndex++);
clipContext._layoutChannelNo = channelNo;
clipContext._layoutBounds.x = 0.0;
clipContext._layoutBounds.y = 0.0;
clipContext._layoutBounds.width = 1.0;
clipContext._layoutBounds.height = 1.0;
clipContext._bufferIndex = renderTextureNo;
} else if (layoutCount == 2) {
for (let i = 0; i < layoutCount; i++) {
let xpos: number = i % 2;
// 小数点は切り捨てる
xpos = ~~xpos;
const cc: CubismClippingContext =
this._clippingContextListForMask.at(curClipIndex++);
cc._layoutChannelNo = channelNo;
// UVを2つに分解して使う
cc._layoutBounds.x = xpos * 0.5;
cc._layoutBounds.y = 0.0;
cc._layoutBounds.width = 0.5;
cc._layoutBounds.height = 1.0;
cc._bufferIndex = renderTextureNo;
}
} else if (layoutCount <= 4) {
// 4分割して使う
for (let i = 0; i < layoutCount; i++) {
let xpos: number = i % 2;
let ypos: number = i / 2;
// 小数点は切り捨てる
xpos = ~~xpos;
ypos = ~~ypos;
const cc = this._clippingContextListForMask.at(curClipIndex++);
cc._layoutChannelNo = channelNo;
cc._layoutBounds.x = xpos * 0.5;
cc._layoutBounds.y = ypos * 0.5;
cc._layoutBounds.width = 0.5;
cc._layoutBounds.height = 0.5;
cc._bufferIndex = renderTextureNo;
}
} else if (layoutCount <= layoutCountMaxValue) {
// 9分割して使う
for (let i = 0; i < layoutCount; i++) {
let xpos = i % 3;
let ypos = i / 3;
// 小数点は切り捨てる
xpos = ~~xpos;
ypos = ~~ypos;
const cc: CubismClippingContext =
this._clippingContextListForMask.at(curClipIndex++);
cc._layoutChannelNo = channelNo;
cc._layoutBounds.x = xpos / 3.0;
cc._layoutBounds.y = ypos / 3.0;
cc._layoutBounds.width = 1.0 / 3.0;
cc._layoutBounds.height = 1.0 / 3.0;
cc._bufferIndex = renderTextureNo;
}
} else {
// マスクの制限枚数を超えた場合の処理
CubismLogError(
'not supported mask count : {0}\n[Details] render texture count : {1}, mask count : {2}',
usingClipCount - useClippingMaskMaxCount,
this._renderTextureCount,
usingClipCount
);
// SetupShaderProgramでオーバーアクセスが発生するので仮で数値を入れる
// もちろん描画結果は正しいものではなくなる
for (let index = 0; index < layoutCount; index++) {
const cc: CubismClippingContext =
this._clippingContextListForMask.at(curClipIndex++);
cc._layoutChannelNo = 0;
cc._layoutBounds.x = 0.0;
cc._layoutBounds.y = 0.0;
cc._layoutBounds.width = 1.0;
cc._layoutBounds.height = 1.0;
cc._bufferIndex = 0;
}
}
}
}
}
/**
* カラーバッファを取得する
* @return カラーバッファ
*/
public getColorBuffer(): csmVector<WebGLTexture> {
return this._maskColorBuffers;
}
/**
* 画面描画に使用するクリッピングマスクのリストを取得する
* @return 画面描画に使用するクリッピングマスクのリスト
*/
public getClippingContextListForDraw(): csmVector<CubismClippingContext> {
return this._clippingContextListForDraw;
}
/**
* マスクの合計数をカウント
* @returns
*/
public getClippingMaskCount(): number {
return this._clippingContextListForMask.getSize();
}
/**
* クリッピングマスクバッファのサイズを設定する
* @param size クリッピングマスクバッファのサイズ
*/
public setClippingMaskBufferSize(size: number): void {
this._clippingMaskBufferSize = size;
}
/**
* クリッピングマスクバッファのサイズを取得する
* @return クリッピングマスクバッファのサイズ
*/
public getClippingMaskBufferSize(): number {
return this._clippingMaskBufferSize;
}
/**
* このバッファのレンダーテクスチャの枚数を取得する
* @return このバッファのレンダーテクスチャの枚数
*/
public getRenderTextureCount(): number {
return this._renderTextureCount;
}
public _currentMaskRenderTexture: WebGLFramebuffer; // マスク用レンダーテクスチャのアドレス
public _maskRenderTextures: csmVector<WebGLFramebuffer>; // レンダーテクスチャのリスト
public _maskColorBuffers: csmVector<WebGLTexture>; // マスク用カラーバッファーのアドレスのリスト
public _currentFrameNo: number; // マスクテクスチャに与えるフレーム番号
public _channelColors: csmVector<CubismTextureColor>;
public _maskTexture: CubismRenderTextureResource; // マスク用のテクスチャリソースのリスト
public _clippingContextListForMask: csmVector<CubismClippingContext>; // マスク用クリッピングコンテキストのリスト
public _clippingContextListForDraw: csmVector<CubismClippingContext>; // 描画用クリッピングコンテキストのリスト
public _clippingMaskBufferSize: number; // クリッピングマスクのバッファサイズ(初期値:256)
public _renderTextureCount: number; // 生成するレンダーテクスチャの枚数
private _tmpMatrix: CubismMatrix44; // マスク計算用の行列
private _tmpMatrixForMask: CubismMatrix44; // マスク計算用の行列
private _tmpMatrixForDraw: CubismMatrix44; // マスク計算用の行列
private _tmpBoundsOnModel: csmRect; // マスク配置計算用の矩形
private _clearedFrameBufferflags: csmVector<boolean>; //マスクのクリアフラグの配列
gl: WebGLRenderingContext; // WebGLレンダリングコンテキスト
}
/**
* レンダーテクスチャのリソースを定義する構造体
* クリッピングマスクで使用する
*/
export class CubismRenderTextureResource {
/**
* 引数付きコンストラクタ
* @param frameNo レンダラーのフレーム番号
* @param texture テクスチャのアドレス
*/
public constructor(frameNo: number, texture: csmVector<WebGLFramebuffer>) {
this.frameNo = frameNo;
this.textures = texture;
}
public frameNo: number; // レンダラのフレーム番号
public textures: csmVector<WebGLFramebuffer>; // テクスチャのアドレス
}
/**
* クリッピングマスクのコンテキスト
*/
export class CubismClippingContext {
/**
* 引数付きコンストラクタ
*/
public constructor(
manager: CubismClippingManager_WebGL,
clippingDrawableIndices: Int32Array,
clipCount: number
) {
this._owner = manager;
// クリップしている(=マスク用の)Drawableのインデックスリスト
this._clippingIdList = clippingDrawableIndices;
// マスクの数
this._clippingIdCount = clipCount;
this._allClippedDrawRect = new csmRect();
this._layoutBounds = new csmRect();
this._clippedDrawableIndexList = [];
this._matrixForMask = new CubismMatrix44();
this._matrixForDraw = new CubismMatrix44();
this._bufferIndex = 0;
}
/**
* デストラクタ相当の処理
*/
public release(): void {
if (this._layoutBounds != null) {
this._layoutBounds = null;
}
if (this._allClippedDrawRect != null) {
this._allClippedDrawRect = null;
}
if (this._clippedDrawableIndexList != null) {
this._clippedDrawableIndexList = null;
}
}
/**
* このマスクにクリップされる描画オブジェクトを追加する
*
* @param drawableIndex クリッピング対象に追加する描画オブジェクトのインデックス
*/
public addClippedDrawable(drawableIndex: number) {
this._clippedDrawableIndexList.push(drawableIndex);
}
/**
* このマスクを管理するマネージャのインスタンスを取得する
* @return クリッピングマネージャのインスタンス
*/
public getClippingManager(): CubismClippingManager_WebGL {
return this._owner;
}
public setGl(gl: WebGLRenderingContext): void {
this._owner.setGL(gl);
}
public _isUsing: boolean; // 現在の描画状態でマスクの準備が必要ならtrue
public readonly _clippingIdList: Int32Array; // クリッピングマスクのIDリスト
public _clippingIdCount: number; // クリッピングマスクの数
public _layoutChannelNo: number; // RGBAのいずれのチャンネルにこのクリップを配置するか(0:R, 1:G, 2:B, 3:A)
public _layoutBounds: csmRect; // マスク用チャンネルのどの領域にマスクを入れるか(View座標-1~1, UVは0~1に直す)
public _allClippedDrawRect: csmRect; // このクリッピングで、クリッピングされるすべての描画オブジェクトの囲み矩形(毎回更新)
public _matrixForMask: CubismMatrix44; // マスクの位置計算結果を保持する行列
public _matrixForDraw: CubismMatrix44; // 描画オブジェクトの位置計算結果を保持する行列
public _clippedDrawableIndexList: number[]; // このマスクにクリップされる描画オブジェクトのリスト
public _bufferIndex: number; // このマスクが割り当てられるレンダーテクスチャ(フレームバッファ)やカラーバッファのインデックス
private _owner: CubismClippingManager_WebGL; // このマスクを管理しているマネージャのインスタンス
}
export class CubismRendererProfile_WebGL {
private setGlEnable(index: GLenum, enabled: GLboolean): void {
if (enabled) this.gl.enable(index);
else this.gl.disable(index);
}
private setGlEnableVertexAttribArray(
index: GLuint,
enabled: GLboolean
): void {
if (enabled) this.gl.enableVertexAttribArray(index);
else this.gl.disableVertexAttribArray(index);
}
public save(): void {
if (this.gl == null) {
CubismLogError(
"'gl' is null. WebGLRenderingContext is required.\nPlease call 'CubimRenderer_WebGL.startUp' function."
);
return;
}
//-- push state --
this._lastArrayBufferBinding = this.gl.getParameter(
this.gl.ARRAY_BUFFER_BINDING
);
this._lastArrayBufferBinding = this.gl.getParameter(
this.gl.ELEMENT_ARRAY_BUFFER_BINDING
);
this._lastProgram = this.gl.getParameter(this.gl.CURRENT_PROGRAM);
this._lastActiveTexture = this.gl.getParameter(this.gl.ACTIVE_TEXTURE);
this.gl.activeTexture(this.gl.TEXTURE1); //テクスチャユニット1をアクティブに(以後の設定対象とする)
this._lastTexture1Binding2D = this.gl.getParameter(
this.gl.TEXTURE_BINDING_2D
);
this.gl.activeTexture(this.gl.TEXTURE0); //テクスチャユニット0をアクティブに(以後の設定対象とする)
this._lastTexture0Binding2D = this.gl.getParameter(
this.gl.TEXTURE_BINDING_2D
);
this._lastVertexAttribArrayEnabled[0] = this.gl.getVertexAttrib(
0,
this.gl.VERTEX_ATTRIB_ARRAY_ENABLED
);
this._lastVertexAttribArrayEnabled[1] = this.gl.getVertexAttrib(
1,
this.gl.VERTEX_ATTRIB_ARRAY_ENABLED
);
this._lastVertexAttribArrayEnabled[2] = this.gl.getVertexAttrib(
2,
this.gl.VERTEX_ATTRIB_ARRAY_ENABLED
);
this._lastVertexAttribArrayEnabled[3] = this.gl.getVertexAttrib(
3,
this.gl.VERTEX_ATTRIB_ARRAY_ENABLED
);
this._lastScissorTest = this.gl.isEnabled(this.gl.SCISSOR_TEST);
this._lastStencilTest = this.gl.isEnabled(this.gl.STENCIL_TEST);
this._lastDepthTest = this.gl.isEnabled(this.gl.DEPTH_TEST);
this._lastCullFace = this.gl.isEnabled(this.gl.CULL_FACE);
this._lastBlend = this.gl.isEnabled(this.gl.BLEND);
this._lastFrontFace = this.gl.getParameter(this.gl.FRONT_FACE);
this._lastColorMask = this.gl.getParameter(this.gl.COLOR_WRITEMASK);
// backup blending
this._lastBlending[0] = this.gl.getParameter(this.gl.BLEND_SRC_RGB);
this._lastBlending[1] = this.gl.getParameter(this.gl.BLEND_DST_RGB);
this._lastBlending[2] = this.gl.getParameter(this.gl.BLEND_SRC_ALPHA);
this._lastBlending[3] = this.gl.getParameter(this.gl.BLEND_DST_ALPHA);
// モデル描画直前のFBOとビューポートを保存
this._lastFBO = this.gl.getParameter(this.gl.FRAMEBUFFER_BINDING);
this._lastViewport = this.gl.getParameter(this.gl.VIEWPORT);
}
public restore(): void {
if (this.gl == null) {
CubismLogError(
"'gl' is null. WebGLRenderingContext is required.\nPlease call 'CubimRenderer_WebGL.startUp' function."
);
return;
}
this.gl.useProgram(this._lastProgram);
this.setGlEnableVertexAttribArray(0, this._lastVertexAttribArrayEnabled[0]);
this.setGlEnableVertexAttribArray(1, this._lastVertexAttribArrayEnabled[1]);
this.setGlEnableVertexAttribArray(2, this._lastVertexAttribArrayEnabled[2]);
this.setGlEnableVertexAttribArray(3, this._lastVertexAttribArrayEnabled[3]);
this.setGlEnable(this.gl.SCISSOR_TEST, this._lastScissorTest);
this.setGlEnable(this.gl.STENCIL_TEST, this._lastStencilTest);
this.setGlEnable(this.gl.DEPTH_TEST, this._lastDepthTest);
this.setGlEnable(this.gl.CULL_FACE, this._lastCullFace);
this.setGlEnable(this.gl.BLEND, this._lastBlend);
this.gl.frontFace(this._lastFrontFace);
this.gl.colorMask(
this._lastColorMask[0],
this._lastColorMask[1],
this._lastColorMask[2],
this._lastColorMask[3]
);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this._lastArrayBufferBinding); //前にバッファがバインドされていたら破棄する必要がある
this.gl.bindBuffer(
this.gl.ELEMENT_ARRAY_BUFFER,
this._lastElementArrayBufferBinding
);
this.gl.activeTexture(this.gl.TEXTURE1); //テクスチャユニット1を復元
this.gl.bindTexture(this.gl.TEXTURE_2D, this._lastTexture1Binding2D);
this.gl.activeTexture(this.gl.TEXTURE0); //テクスチャユニット0を復元
this.gl.bindTexture(this.gl.TEXTURE_2D, this._lastTexture0Binding2D);
this.gl.activeTexture(this._lastActiveTexture);
this.gl.blendFuncSeparate(
this._lastBlending[0],
this._lastBlending[1],
this._lastBlending[2],
this._lastBlending[3]
);
}
public setGl(gl: WebGLRenderingContext): void {
this.gl = gl;
}
constructor() {
this._lastVertexAttribArrayEnabled = new Array<GLboolean>(4);
this._lastColorMask = new Array<GLboolean>(4);
this._lastBlending = new Array<GLint>(4);
this._lastViewport = new Array<GLint>(4);
}
private _lastArrayBufferBinding: GLint; ///< モデル描画直前の頂点バッファ
private _lastElementArrayBufferBinding: GLint; ///< モデル描画直前のElementバッファ
private _lastProgram: GLint; ///< モデル描画直前のシェーダプログラムバッファ
private _lastActiveTexture: GLint; ///< モデル描画直前のアクティブなテクスチャ
private _lastTexture0Binding2D: GLint; ///< モデル描画直前のテクスチャユニット0
private _lastTexture1Binding2D: GLint; ///< モデル描画直前のテクスチャユニット1
private _lastVertexAttribArrayEnabled: GLboolean[]; ///< モデル描画直前のテクスチャユニット1
private _lastScissorTest: GLboolean; ///< モデル描画直前のGL_VERTEX_ATTRIB_ARRAY_ENABLEDパラメータ
private _lastBlend: GLboolean; ///< モデル描画直前のGL_SCISSOR_TESTパラメータ
private _lastStencilTest: GLboolean; ///< モデル描画直前のGL_STENCIL_TESTパラメータ
private _lastDepthTest: GLboolean; ///< モデル描画直前のGL_DEPTH_TESTパラメータ
private _lastCullFace: GLboolean; ///< モデル描画直前のGL_CULL_FACEパラメータ
private _lastFrontFace: GLint; ///< モデル描画直前のGL_CULL_FACEパラメータ
private _lastColorMask: GLboolean[]; ///< モデル描画直前のGL_COLOR_WRITEMASKパラメータ
private _lastBlending: GLint[]; ///< モデル描画直前のカラーブレンディングパラメータ
private _lastFBO: GLint; ///< モデル描画直前のフレームバッファ
private _lastViewport: GLint[]; ///< モデル描画直前のビューポート
gl: WebGLRenderingContext;
}
/**
* WebGL用のシェーダープログラムを生成・破棄するクラス
* シングルトンなクラスであり、CubismShader_WebGL.getInstanceからアクセスする。
*/
export class CubismShader_WebGL {
/**
* インスタンスを取得する(シングルトン)
* @return インスタンス
*/
public static getInstance(): CubismShader_WebGL {
if (s_instance == null) {
s_instance = new CubismShader_WebGL();
return s_instance;
}
return s_instance;
}
/**
* インスタンスを開放する(シングルトン)
*/
public static deleteInstance(): void {
if (s_instance) {
s_instance.release();
s_instance = null;
}
}
/**
* privateなコンストラクタ
*/
private constructor() {
this._shaderSets = new csmVector<CubismShaderSet>();
}
/**
* デストラクタ相当の処理
*/
public release(): void {
this.releaseShaderProgram();
}
/**
* シェーダープログラムの一連のセットアップを実行する
* @param renderer レンダラのインスタンス
* @param textureId GPUのテクスチャID
* @param vertexCount ポリゴンメッシュの頂点数
* @param vertexArray ポリゴンメッシュの頂点配列
* @param indexArray インデックスバッファの頂点配列
* @param uvArray uv配列
* @param opacity 不透明度
* @param colorBlendMode カラーブレンディングのタイプ
* @param baseColor ベースカラー
* @param isPremultipliedAlpha 乗算済みアルファかどうか
* @param matrix4x4 Model-View-Projection行列
* @param invertedMask マスクを反転して使用するフラグ
*/
public setupShaderProgram(
renderer: CubismRenderer_WebGL,
textureId: WebGLTexture,
vertexCount: number,
vertexArray: Float32Array,
indexArray: Uint16Array,
uvArray: Float32Array,
bufferData: {
vertex: WebGLBuffer;
uv: WebGLBuffer;
index: WebGLBuffer;
},
opacity: number,
colorBlendMode: CubismBlendMode,
baseColor: CubismTextureColor,
multiplyColor: CubismTextureColor,
screenColor: CubismTextureColor,
isPremultipliedAlpha: boolean,
matrix4x4: CubismMatrix44,
invertedMask: boolean
): void {
if (!isPremultipliedAlpha) {
CubismLogError('NoPremultipliedAlpha is not allowed');
}
if (this._shaderSets.getSize() == 0) {
this.generateShaders();
}
// Blending
let SRC_COLOR: number;
let DST_COLOR: number;
let SRC_ALPHA: number;
let DST_ALPHA: number;
if (renderer.getClippingContextBufferForMask() != null) {
// マスク生成時
const shaderSet: CubismShaderSet = this._shaderSets.at(
ShaderNames.ShaderNames_SetupMask
);
this.gl.useProgram(shaderSet.shaderProgram);
// テクスチャ設定
this.gl.activeTexture(this.gl.TEXTURE0);
this.gl.bindTexture(this.gl.TEXTURE_2D, textureId);
this.gl.uniform1i(shaderSet.samplerTexture0Location, 0);
// 頂点配列の設定(VBO)
if (bufferData.vertex == null) {
bufferData.vertex = this.gl.createBuffer();
}
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.vertex);
this.gl.bufferData(
this.gl.ARRAY_BUFFER,
vertexArray,
this.gl.DYNAMIC_DRAW
);
this.gl.enableVertexAttribArray(shaderSet.attributePositionLocation);
this.gl.vertexAttribPointer(
shaderSet.attributePositionLocation,
2,
this.gl.FLOAT,
false,
0,
0
);
// テクスチャ頂点の設定
if (bufferData.uv == null) {
bufferData.uv = this.gl.createBuffer();
}
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.uv);
this.gl.bufferData(this.gl.ARRAY_BUFFER, uvArray, this.gl.DYNAMIC_DRAW);
this.gl.enableVertexAttribArray(shaderSet.attributeTexCoordLocation);
this.gl.vertexAttribPointer(
shaderSet.attributeTexCoordLocation,
2,
this.gl.FLOAT,
false,
0,
0
);
// チャンネル
const channelNo: number =
renderer.getClippingContextBufferForMask()._layoutChannelNo;
const colorChannel: CubismTextureColor = renderer
.getClippingContextBufferForMask()
.getClippingManager()
.getChannelFlagAsColor(channelNo);
this.gl.uniform4f(
shaderSet.uniformChannelFlagLocation,
colorChannel.R,
colorChannel.G,
colorChannel.B,
colorChannel.A
);
this.gl.uniformMatrix4fv(
shaderSet.uniformClipMatrixLocation,
false,
renderer.getClippingContextBufferForMask()._matrixForMask.getArray()
);
const rect: csmRect =
renderer.getClippingContextBufferForMask()._layoutBounds;
this.gl.uniform4f(
shaderSet.uniformBaseColorLocation,
rect.x * 2.0 - 1.0,
rect.y * 2.0 - 1.0,
rect.getRight() * 2.0 - 1.0,
rect.getBottom() * 2.0 - 1.0
);
this.gl.uniform4f(
shaderSet.uniformMultiplyColorLocation,
multiplyColor.R,
multiplyColor.G,
multiplyColor.B,
multiplyColor.A
);
this.gl.uniform4f(
shaderSet.uniformScreenColorLocation,
screenColor.R,
screenColor.G,
screenColor.B,
screenColor.A
);
SRC_COLOR = this.gl.ZERO;
DST_COLOR = this.gl.ONE_MINUS_SRC_COLOR;
SRC_ALPHA = this.gl.ZERO;
DST_ALPHA = this.gl.ONE_MINUS_SRC_ALPHA;
} // マスク生成以外の場合
else {
const masked: boolean =
renderer.getClippingContextBufferForDraw() != null; // この描画オブジェクトはマスク対象か
const offset: number = masked ? (invertedMask ? 2 : 1) : 0;
let shaderSet: CubismShaderSet = new CubismShaderSet();
switch (colorBlendMode) {
case CubismBlendMode.CubismBlendMode_Normal:
default:
shaderSet = this._shaderSets.at(
ShaderNames.ShaderNames_NormalPremultipliedAlpha + offset
);
SRC_COLOR = this.gl.ONE;
DST_COLOR = this.gl.ONE_MINUS_SRC_ALPHA;
SRC_ALPHA = this.gl.ONE;
DST_ALPHA = this.gl.ONE_MINUS_SRC_ALPHA;
break;
case CubismBlendMode.CubismBlendMode_Additive:
shaderSet = this._shaderSets.at(
ShaderNames.ShaderNames_AddPremultipliedAlpha + offset
);
SRC_COLOR = this.gl.ONE;
DST_COLOR = this.gl.ONE;
SRC_ALPHA = this.gl.ZERO;
DST_ALPHA = this.gl.ONE;
break;
case CubismBlendMode.CubismBlendMode_Multiplicative:
shaderSet = this._shaderSets.at(
ShaderNames.ShaderNames_MultPremultipliedAlpha + offset
);
SRC_COLOR = this.gl.DST_COLOR;
DST_COLOR = this.gl.ONE_MINUS_SRC_ALPHA;
SRC_ALPHA = this.gl.ZERO;
DST_ALPHA = this.gl.ONE;
break;
}
this.gl.useProgram(shaderSet.shaderProgram);
// 頂点配列の設定
if (bufferData.vertex == null) {
bufferData.vertex = this.gl.createBuffer();
}
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.vertex);
this.gl.bufferData(
this.gl.ARRAY_BUFFER,
vertexArray,
this.gl.DYNAMIC_DRAW
);
this.gl.enableVertexAttribArray(shaderSet.attributePositionLocation);
this.gl.vertexAttribPointer(
shaderSet.attributePositionLocation,
2,
this.gl.FLOAT,
false,
0,
0
);
// テクスチャ頂点の設定
if (bufferData.uv == null) {
bufferData.uv = this.gl.createBuffer();
}
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.uv);
this.gl.bufferData(this.gl.ARRAY_BUFFER, uvArray, this.gl.DYNAMIC_DRAW);
this.gl.enableVertexAttribArray(shaderSet.attributeTexCoordLocation);
this.gl.vertexAttribPointer(
shaderSet.attributeTexCoordLocation,
2,
this.gl.FLOAT,
false,
0,
0
);
if (masked) {
this.gl.activeTexture(this.gl.TEXTURE1);
const tex: WebGLTexture = renderer
.getClippingContextBufferForDraw()
.getClippingManager()
.getColorBuffer()
.at(renderer.getClippingContextBufferForDraw()._bufferIndex);
this.gl.bindTexture(this.gl.TEXTURE_2D, tex);
this.gl.uniform1i(shaderSet.samplerTexture1Location, 1);
// view座標をClippingContextの座標に変換するための行列を設定
this.gl.uniformMatrix4fv(
shaderSet.uniformClipMatrixLocation,
false,
renderer.getClippingContextBufferForDraw()._matrixForDraw.getArray()
);
// 使用するカラーチャンネルを設定
const channelNo: number =
renderer.getClippingContextBufferForDraw()._layoutChannelNo;
const colorChannel: CubismTextureColor = renderer
.getClippingContextBufferForDraw()
.getClippingManager()
.getChannelFlagAsColor(channelNo);
this.gl.uniform4f(
shaderSet.uniformChannelFlagLocation,
colorChannel.R,
colorChannel.G,
colorChannel.B,
colorChannel.A
);
}
// テクスチャ設定
this.gl.activeTexture(this.gl.TEXTURE0);