Blender Hack Blog

オープンソースの総合3DCGソフトウェアのBlenderのコード解析や開発を記録していきます。

実験: ワイヤーフレームの色を変更してみる

はじめに

今回は、Blenderワイヤーフレームの色をHackして変更してみます。 GLSL(OpenGL Shading Language)の知識が役に立ちました。

使用したコード

master (2.93.0)を使用しました。 https://developer.blender.org/rBd447bd3e4a9a793364b5f4951ad280fe0293d79e

ワイヤーフレームが描画される仕組み

頂点シェーダとフラグメントシェーダでモデルのワイヤーフレームが描画されます。 頂点シェーダで定義されているfinalColorワイヤーフレームの色がセットされて、フラグメントシェーダでfinalColorを参照して最終的な色(fragColor)にセットされます。

source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl

out vec4 finalColor;
[...]
void main()
{
[...]
  vec3 rim_col, wire_col;
  if (isObjectColor || isRandomColor) {
    wire_object_color_get(rim_col, wire_col);
  }
  else {
    wire_color_get(rim_col, wire_col);
  }

  facing = clamp(abs(facing), 0.0, 1.0);

  /* Do interpolation in a non-linear space to have a better visual result. */
  rim_col = pow(rim_col, vec3(1.0 / 2.2));
  wire_col = pow(wire_col, vec3(1.0 / 2.2));
  vec3 final_front_col = mix(rim_col, wire_col, 0.35);
  finalColor.rgb = mix(rim_col, final_front_col, facing);
  finalColor.rgb = pow(finalColor.rgb, vec3(2.2));
  finalColor.a = wireOpacity;
  finalColor.rgb *= wireOpacity;
}

source/blender/draw/engines/overlay/shaders/wireframe_frag.glsl

in vec4 finalColor;
[...]
void main()
{
[...]
  fragColor = finalColor;

ちなみに、wireframe_vert.glslwireframe_frag.glslはビルド時に、const char[]型のdatatoc_wireframe_vert_glsldatatoc_wireframe_frag_glslにそれぞれ変換されるようです。 そして、OVERLAY_shader_wireframe()でシェーダープログラムとしてロードされます。

source/blender/draw/engines/overlay/overlay_shader.c

GPUShader *OVERLAY_shader_wireframe(bool custom_bias)
{
  const DRWContextState *draw_ctx = DRW_context_state_get();
  const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg];
  OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
  if (!sh_data->wireframe[custom_bias]) {
    sh_data->wireframe[custom_bias] = GPU_shader_create_from_arrays({
        .vert = (const char *[]){sh_cfg->lib,
                                 datatoc_common_view_lib_glsl,
                                 datatoc_common_globals_lib_glsl,
                                 datatoc_gpu_shader_common_obinfos_lib_glsl,
                                 datatoc_wireframe_vert_glsl,
                                 NULL},
        .frag = (const char *[]){datatoc_common_view_lib_glsl,
                                 datatoc_common_globals_lib_glsl,
                                 datatoc_wireframe_frag_glsl,
                                 NULL},
        .defs = (const char *[]){sh_cfg->def,
                                 custom_bias ? "#define CUSTOM_DEPTH_BIAS\n" : NULL,
                                 NULL},
    });
  }
  return sh_data->wireframe[custom_bias];
}

ワイヤーフレームの色をセットしている場所の特定

wire_color_get()を見ると、colorWireという変数が見つかりました。この変数にワイヤーフレームの色がセットされていそうです。

void wire_color_get(out vec3 rim_col, out vec3 wire_col)
{
[...]
  else if (is_selected && useColoring) {
    if (isTransform) {
      rim_col = colorTransform.rgb;
    }
    else if (is_active) {
      rim_col = colorActive.rgb;
    }
    else {
      rim_col = colorSelect.rgb;
    }
    wire_col = colorWire.rgb;
  }
  else {
    rim_col = colorWire.rgb;
    wire_col = colorBackground.rgb;
  }
}

colorWiregrepしてみると、以下のuniform変数として定義されていることが分かりました。

source/blender/draw/intern/shaders/common_globals_lib.glsl

/* keep in sync with GlobalsUboStorage */
layout(std140) uniform globalsBlock
{
  vec4 colorWire;
  vec4 colorWireEdit;
  vec4 colorActive;
  vec4 colorSelect;
[...]

もう少し探索を進めると、uniform変数にセットしていそうなコードが見つかりました。 UI_GetThemeColor4fv(TH_WIRE, gb->colorWire);にbreak pointをセットしてみます。 デバッガで確認すると、初回実行時には、ゼロ初期化されていて、DRW_globals_update(void)の呼び出し後は、 gb->colorWireに(0, 0, 0, 1)がセットされました。

source/blender/draw/intern/draw_common.c

void DRW_globals_update(void)
{
  GlobalsUboStorage *gb = &G_draw.block;

  UI_GetThemeColor4fv(TH_WIRE, gb->colorWire);
  UI_GetThemeColor4fv(TH_WIRE_EDIT, gb->colorWireEdit);
  UI_GetThemeColor4fv(TH_ACTIVE, gb->colorActive);
  UI_GetThemeColor4fv(TH_SELECT, gb->colorSelect);
[...]

コードの修正

colorWireは、float[4]でそれぞれの要素はRGBAに対応しているようです。 そこで、rgba = (1, 0, 0, 1)、つまり赤をセットしてみます。

diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c
index 132b5274517..db7501e6a94 100644
--- a/source/blender/draw/intern/draw_common.c
+++ b/source/blender/draw/intern/draw_common.c
@@ -53,7 +53,12 @@ void DRW_globals_update(void)
 {
   GlobalsUboStorage *gb = &G_draw.block;
 
-  UI_GetThemeColor4fv(TH_WIRE, gb->colorWire);
+  gb->colorWire[0] = 1.0;
+  gb->colorWire[1] = 0.0;
+  gb->colorWire[2] = 0.0;
+  gb->colorWire[3] = 1.0;
+
+  //UI_GetThemeColor4fv(TH_WIRE, gb->colorWire);
   UI_GetThemeColor4fv(TH_WIRE_EDIT, gb->colorWireEdit);
   UI_GetThemeColor4fv(TH_ACTIVE, gb->colorActive);
   UI_GetThemeColor4fv(TH_SELECT, gb->colorSelect);

ビルドと実行結果

ビルドして実行し、ワイヤーフレームで表示すると無事ワイヤーフレームの色が赤くなりました。

f:id:fixme:20210223110934p:plain
ワイヤーフレームの色が変更された

関連情報

ワイヤーフレームの不透明度をUIから変更できるようにする修正

⚙ D7622 Add An Opacity Slider to Overlay Wireframe