toya desk

toya desk

Share this post

toya desk
toya desk
8thWall)カスタムシェーダーを適用する(ポストプロセス編)
8thWall

8thWall)カスタムシェーダーを適用する(ポストプロセス編)

Applying Custom-Shader in postprocessing way.

toya's avatar
toya
Jul 05, 2025
∙ Paid

Share this post

toya desk
toya desk
8thWall)カスタムシェーダーを適用する(ポストプロセス編)
Share

やり方+結論だけ知りたい人向けの本記事のまとめ。

「8th Wallでポストプロセス的にルックを調整したい」場合
⇒「カメラ」に「shader-postprocessing.js」などをコンポーネントとして追加する

ということで以下経緯や詳細。


経緯

Three.jsやりたい。

でも。

Blenderもまだまだ把握できてない機能が山ほどあるし手を付けられていないアドオンも積みあがってるし、UE5もいまだに初心者から抜け出し切れていないし、Unityも業務で使うから検証しないといけないこといっぱいあるし、AIもClaude CodeがーGemini-CLIと仲良くなりたいーDevinもCursorも、あ、Replitも課金してるから使い倒さないといけないし動画生成も要把握でGoogle ColabもPro課金しちゃったし・・・

時間無い………!!!!!
もう学習に割く時間が、ない.....!!!
(注記:AIの件がこうなる前から3DCG従事者は割と同じ状況だった)

ああでもThree.js、使えたら,,,というかシェーダーも理解できてた方が、、、どうやって学習を進めれば、、、と悩んでいたのですが、ふと。

「あれ?8th WallのコンポーネントはJavaScript系で実装していく・・・ということはthree.jsもいけるよね?これでシェーダーも作れるよね?というか8th Wallでルックの調整(toon調で表現したり)ってどうやるんだ?」

⇒「助けてClaude先生!!」


調べてわかったこと

……ということでClaudeにいろいろ教えてもらって、カスタムシェーダーを作ってもらいました。

また、8th Wallで作品作り・開発していれば、自然とthree.jsやシェーダーのスクリプトにも触れていくことになるので、学習時間の確保はこれでできるかな、となりました。

まだ検証しきれていませんが、8th Wallでは

  • Unityでよくやるようなオブジェクトのマテリアルごとのシェーダー適用

  • UE5のポストプロセスやBlenderでのコンポジットのような、シーン全体にシェーダーを適用

の両方ができるようです。
今回はその「後者」のポストプロセス式のシェーダーを利用するケースの話です。

筆者はこの辺の専門知識は独学・ゆっくり学習を進めています。
専門用語が怪しい場合はご容赦を+置換してお読みください🙏


作ったシェーダーサンプル

もとい、Claudeに作ってもらった「toon-postprocess.js」はこちら。

検証用に出力してもらった最低限のものになっていると思います。
このまま流用していただいて問題ありません。
何度か「エラー出てるよー、直してたもれ!」としているので修正が重なったものです。
「UnitychanToonShaderみたいの作って!」とプロンプトしました。


また、こちらのGithubにも(自分のメモ用として)公開しておきます。
(Githubのほうが最新版になっているかもしれない)

// toon-postprocess.js の修正版(テクスチャ対応)
import * as ecs from '@8thwall/ecs'

let isInitialized = false

ecs.registerComponent({
  name: 'toon-postprocess',
  schema: {
    intensity: ecs.f32,
    steps: ecs.f32
  },
  schemaDefaults: {
    intensity: 0.8,
    steps: 4.0
  },
  
  add: (world, component) => {
    console.log('Toon postprocess component added!')
    
    setTimeout(() => {
      if (!isInitialized) {
        setupToonEffectWithTextures(world, component)
        isInitialized = true
      }
    }, 2000)
  }
})

function setupToonEffectWithTextures(world, component) {
  console.log('Setting up toon effect with texture support...')
  
  const intensity = component.schema.intensity
  const steps = component.schema.steps
  
  const scene = world.scene
  
  if (scene) {
    scene.traverse((child) => {
      if (child.isMesh && child.material && child.name) {
        if (child.name.includes('Camera') || 
            child.name.includes('Light') || 
            child.name.includes('Helper') ||
            child.name.includes('Gizmo')) {
          return
        }
        
        console.log('Applying texture-aware toon shader to:', child.name)
        
        try {
          const originalMaterial = child.material
          
          // テクスチャ対応のカスタムシェーダー
          const toonShader = new THREE.ShaderMaterial({
            uniforms: {
              baseColor: { value: originalMaterial.color || new THREE.Color(0x888888) },
              colorMap: { value: originalMaterial.map || null },
              lightDirection: { value: new THREE.Vector3(1, 1, 1).normalize() },
              intensity: { value: intensity },
              steps: { value: steps },
              hasTexture: { value: originalMaterial.map ? 1.0 : 0.0 }
            },
            vertexShader: `
              varying vec3 vNormal;
              varying vec2 vUv;
              
              void main() {
                vNormal = normalize(normalMatrix * normal);
                vUv = uv;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
              }
            `,
            fragmentShader: `
              uniform vec3 baseColor;
              uniform sampler2D colorMap;
              uniform vec3 lightDirection;
              uniform float intensity;
              uniform float steps;
              uniform float hasTexture;
              varying vec3 vNormal;
              varying vec2 vUv;
              
              void main() {
                // テクスチャ色を取得
                vec3 textureColor = baseColor;
                if (hasTexture > 0.5) {
                  vec4 texColor = texture2D(colorMap, vUv);
                  textureColor = texColor.rgb * baseColor;
                }
                
                // ライティング計算
                float lightIntensity = dot(vNormal, lightDirection);
                lightIntensity = (lightIntensity + 1.0) * 0.5; // -1〜1 を 0〜1 に変換
                
                // ステップ化
                float stepSize = 1.0 / steps;
                float steppedIntensity = ceil(lightIntensity / stepSize) * stepSize;
                
                // intensityで効果の強さを調整
                float finalIntensity = mix(lightIntensity, steppedIntensity, intensity);
                finalIntensity = max(finalIntensity, 0.3); // 最低限の明るさ
                
                // 最終色 = テクスチャ色 × ライティング
                vec3 finalColor = textureColor * finalIntensity;
                
                gl_FragColor = vec4(finalColor, 1.0);
              }
            `,
            transparent: originalMaterial.transparent,
            side: originalMaterial.side || THREE.FrontSide
          })
          
          child.material = toonShader
          console.log('Applied texture-aware toon shader to:', child.name)
          
        } catch (error) {
          console.error('Failed to apply shader to:', child.name, error)
        }
      }
    })
    
    console.log('Texture-aware toon effect setup completed!')
  }
}

8th Wallにこのスクリプトを適用する

  1. このスクリプトを以下のいずれかの方法で8th Wallのプロジェクトにアップロードします。

    • 上述の「toon-postprocess.js」をローカルに保存して、プロジェクトで「アップロード」

    • プロジェクト内で新規にコンポーネント・ファイルを作成し、上述のスクリプトをコピペ(やり方は過去記事を参照してください)

  1. 続いて、「カメラ」にこのコンポーネントを適用します

    • こちらも過去記事の「ボタンにコンポーネントを追加」のやり方と同じです。

  2. すると、「カメラ」選択時にインスペクターに以下の表示が現れます。


設定可能パラメータ

現在(この記事公開時)、調整可能なパラメータは以下の2点。

スクリプトを出力したCluadeによる説明。

▼試した感じ。

中央の青いキャラクターはDiffuse(albedo、ベースカラー)のみなので綺麗に出ています。
(後ろに置いている板ポリの透過ができてない+PBRのテクスチャも出てませんね…こうやってシェーダーの理解が深まっていくのか…と言う感覚)

①オリジナル、②ポストプロセス適用前、③上述のアニメ/マンガスタイル、④ソフトなトゥーン、⑤ハイコントラスト

▼Claudeが勉強用にまとめてくれたメモ。


ということで。
8th Wallであれば、

  • 作品展示

  • Vibe Codingで開発

  • WebGL/three.js/シェーダー周りの学習

  • AR開発

などいろいろできて「一石2+n鳥」行けそうだな、と思っています。

おまけ:今回の配布GLB

以下はサブスクしてくださっている向けのコンテンツです。
今回は上の図に出てきているこちらのローテーブル(コーヒーテーブル)。
Boothでも販売中です。

Keep reading with a 7-day free trial

Subscribe to toya desk to keep reading this post and get 7 days of free access to the full post archives.

Already a paid subscriber? Sign in
© 2025 toya
Privacy ∙ Terms ∙ Collection notice
Start writingGet the app
Substack is the home for great culture

Share