writer : Tomoya Okada
【fragmentShader】特定の色を除去し、透過させる
こんにちは、okadaです。
今回はfragmentShaderを使って、特定の色を除去する方法についてご紹介します。
概要
▼読書の想定
- Three.jsの利用経験がある
- Shaderに少し触れたことがある
▼デモサイト
https://tech75-exclude-certain-colors.vercel.app/

具体的なコード
まずは主要な部分のコードを以下に掲載します。
後ほど解説する箇所には番号を振っています。
async setObject() {
//①
const texture = this.loader.load("/images/image.png");
const geo = new THREE.PlaneGeometry(0.13, 0.2);
const mat = new THREE.ShaderMaterial({
uniforms: {
//②
t_tex: { value: texture },
u_targetColor: { value: new THREE.Color(0xffffff).toArray() },
u_threshold: { value: 0.2 },
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`,
fragmentShader: `
precision mediump float;
uniform sampler2D t_tex;
uniform vec3 u_targetColor;
uniform float u_threshold;
varying vec2 vUv;
void main() {
vec2 uv = vUv;
//③
vec4 Tex = texture2D(t_tex,uv);
//④
float dist = distance(Tex.rgb, u_targetColor);
if (dist < u_threshold) {
Tex.a = 0.0;
}
gl_FragColor = Tex;
}
`,
side: THREE.DoubleSide,
transparent: true,
});
const mesh = new THREE.Mesh(geo, mat);
this.stage.scene.add(mesh);
}①について
//①
const texture = this.loader.load("/images/image.png");
const geo = new THREE.PlaneGeometry(0.13, 0.2);
const mat = new THREE.ShaderMaterial({
...(略)
ここでは、テクスチャ・ジオメトリ・マテリアルの定義をしています。
this.loaderは、constructor側で「new THREE.TextureLoader」を設定しています。
今回は画像を加工していくので、ShaderMaterialを使って行っていきます。
②について
uniforms: {
//②
t_tex: { value: texture },
u_targetColor: { value: new THREE.Color(0xffffff).toArray() },
u_threshold: { value: 0.2 },
},Shaderに渡して使用するuniformを定義しています。
t_tex : テクスチャ情報
u_targetColor : 除去する色指定
u_threshold : 閾値
u_targetColorを使って除去する色を決定し、u_thresholdで適用させる範囲を調整するイメージです。
③について
//③
vec4 Tex = texture2D(t_tex,uv);uniformsで設定したテクスチャとvertexShaderから持ってきたuv座標を使って、uv座標におけるテクスチャの色情報を取得しています。
テクスチャを扱う時のテンプレート文のようなものです。
④について
//④
float dist = distance(Tex.rgb, u_targetColor);
if (dist < u_threshold) {
Tex.a = 0.0;
}
gl_FragColor = Tex;
今回の肝になる部分です。
1行目で、「テクスチャの色情報とtargetColorがどのくらい値が離れているか」を算出しています。
- 値が近いほど、テクスチャの色がtargetColorに近い。
- 値が遠いほど、targetColorとは異なる色。
このようなイメージだとわかりやすいと思います。
2行目以降のif文で、先ほど求められた値と閾値を比較して条件に当てはまったもののalphaを0(透過)としています。
例えば、画像を変えて以下のようにすると赤色の成分が除去され透過します。
u_targetColor: { value: new THREE.Color(0xf9311d).toArray() },
u_threshold: { value: 0.5 },
細部の部分が若干残っていますが、大枠は除去できていますね。
今回の実装は背景が複雑ではない単色の方が効果的に使えそうです。
まとめ
fragmentShaderを使った色除去の方法についてのご紹介でした。
私も学習を続けている身なので、もっと良い方法があるかもしれませんが1つの案として覚えておけると良さそうです。
最後までご閲覧いただきありがとうございました。