3D Tilt Shift Blur (Tiefenschärfe)

    • Studio

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • 3D Tilt Shift Blur (Tiefenschärfe)

      In diesem Tutorial wird erklärt wie man einen 3D Tilt Shift Effekt erstellt. Hierbei stelle ich zwei Shader zur freien Verfügung bereit.

      Um einen Tilt Shift Effekt zu erstellen benötigen wir zunächst einen Depth-Buffer. Es wird zwar einer beim starten des 3d Modus erstellt, haben aber keinen direkten Zugriff auf ihn. Deswegen müssen wir mit einer Surface einen eigenen erstellen.

      GML-Quellcode

      1. globalvar sur_depth;
      2. sur_depth = surface_create(surface_get_width(application_surface),surface_get_height(application_surface));

      Der neue Depth-Buffer hat dabei die identische Größe wie der Back-Buffer. Damit wir überall auf die Surface zugreifen können, speichern wir sie in einer globalen Variable.
      Als nächstes Definieren wir noch globale Variablen für die Kamera Position und Richtung:

      GML-Quellcode

      1. globalvar cam_lx, cam_ly, cam_lz, cam_px, cam_py, cam_pz;
      2. cam_px = 0;
      3. cam_py = 0;
      4. cam_pz = 16;

      Als nächstes müssen wir den Depth Buffer vor jedem Zeichenvorgang leeren. Dabei kann folgender Code im Draw Begin Event helfen:

      GML-Quellcode

      1. surface_set_target(sur_depth);
      2. draw_clear(c_white);
      3. surface_reset_target();

      Wenn dies alles geschehen ist können wir alle cam-Variablen in diesem Event setzen. Dies werde ich hier aber nicht weiter erläutern, weil dieser Code stark nach Projekt abweichen kann.
      Nun kommen wir zum Zeichnen der Objekte. Ich werde dazu einen einfachen drehenden Würfel nehmen:

      GML-Quellcode

      1. //Setze die Transformationen
      2. d3d_transform_add_rotation_z(current_time/30);
      3. d3d_transform_add_translation(x,y,0);
      4. //Setze die Projektion für den Back Buffer
      5. d3d_set_projection_ext(cam_px,cam_py,cam_pz,cam_px+cam_lx,cam_py+cam_ly,cam_pz+cam_lz,0,0,1,45,surface_get_width(application_surface)/surface_get_height(application_surface),1,512);
      6. //Zeichne die normale Szene
      7. draw_set_color(c_white);
      8. d3d_draw_block(-8,-8,-8,8,8,8,background_get_texture(bg_earth),1,1);
      9. //Wechsele auf den Depth Buffer
      10. surface_set_target(sur_depth);
      11. //Die Distance wo der Blur Effekt maximal ist und nicht mehr größer wird.
      12. var blur_width = 256;
      13. //Weise die Uniformen Variablen zu
      14. zdis = shader_get_uniform(shd_depth, "zdis");
      15. //Setzt den speziellen Depth Shader ein
      16. shader_set(shd_depth);
      17. shader_set_uniform_f(zdis, blur_width);
      18. //Setze die Projektion nochmal für den Depth Buffer
      19. d3d_set_projection_ext(cam_px,cam_py,cam_pz,cam_px+cam_lx,cam_py+cam_ly,cam_pz+cam_lz,0,0,1,45,surface_get_width(application_surface)/surface_get_height(application_surface),1,blur_width);
      20. //Zeichne auf den Depth Buffer
      21. d3d_draw_block(-8,-8,-8,8,8,8,-1,1,1);
      22. shader_reset();
      23. surface_reset_target();
      24. d3d_transform_set_identity();
      Alles anzeigen

      Ich habe den ganzen Skript hoffentlich verständlich Kommentiert. Im wesentlichen Zeichnen wir das Objekt ganz normal, wie man es auch ohne Effekt machen würde und einmal mit Shader auf den Depth Buffer. Dazu müssen wir immer beim Surface wechsel die Kamera Perspektive setzen, wofür wir die Variablen angelegt haben. Die Variable blur_width vom Shader muss dabei identisch sein mit zfar bei der Projektion für den Depth-Buffer. Dieser Wert kann aber individuell und getrennt von der normalen Projektion eingestellt werden.

      Depth Shader Vertex:

      Quellcode

      1. attribute vec3 in_Position;
      2. attribute vec2 in_TextureCoord;
      3. varying vec2 v_vTexcoord;
      4. varying float v_vDepth;
      5. void main()
      6. {
      7. vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
      8. gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
      9. v_vTexcoord = in_TextureCoord;
      10. v_vDepth = gl_Position.z;
      11. }
      Alles anzeigen


      Depth Shader Fragment:

      Quellcode

      1. varying vec2 v_vTexcoord;
      2. varying float v_vDepth;
      3. uniform float zdis;
      4. void main(void)
      5. {
      6. float nDepth = v_vDepth;
      7. nDepth /= zdis;
      8. gl_FragColor = vec4(nDepth, nDepth, nDepth, 1.);
      9. }


      Man kann natürlich auch direkt ohne Shader auf den Depth Buffer zeichnen, um zum Beispiel Effekte wie schwach spiegelnde Oberflächen zu erstellen, oder für ein Menü kurz alles unscharf erscheinen lassen. Der Kreativität sind dabei keinen Grenzen gesetzt. Es lässt sich sogar komplett in 2D anwenden. Man muss nur beachten das Weiß für maximal Blur steht und Schwarz für kein Blur.

      Kommen wir nun zum Anwenden des Blurs. In diesem Zustand würden wir noch alles ganz normal sehen. Als erstes müssen wir beim Create Event folgende Zeile Code noch hinzufügen:

      GML-Quellcode

      1. application_surface_draw_enable(false);

      Dies deaktiviert das automatische Erneuern des Bildschirms, damit wir dies selbst kontrollieren. Die hat keine Auswirkung auf die Draw GUI Events.
      Wir erstellen nun ein Draw GUI Begin Event. In diesem Event schreiben wir folgenden Code:

      GML-Quellcode

      1. depth_sampler = shader_get_sampler_index(shd_blur, "tex_depth");
      2. uniform_size = shader_get_uniform(shd_blur, "size");
      3. blur_multiplikator = shader_get_uniform(shd_blur, "blur_multiplikator");
      4. tex_depth = surface_get_texture(sur_depth);
      5. shader_set(shd_blur);
      6. texture_set_stage(depth_sampler, tex_depth);
      7. shader_set_uniform_f(blur_multiplikator, 1., 1.);
      8. shader_set_uniform_f(uniform_size, surface_get_width(application_surface), surface_get_height(application_surface));
      9. draw_surface(application_surface, 0, 0);
      10. shader_reset();

      Dies Zeichnet unseren gesamten Inhalt des Back-Buffers mithilfe des Blur Shaders und dem Depth-Buffer. Wesentlich können wir hier den Blur-Multiplikator ändern. Dies ist der multiplikator der Abtastrate, um einen dynamischen Eingriff auf den Blur Effekt zu haben. Wenn der Wert für X oder Y größer ist als 1 wird der Blur Effekt stärker, wodurch aber auch Artefakte entstehen. Standardmäßig ist die Abtastrate 16x16 Pixel pro Pixel, sodass jeder Pixel einmal abgefragt wird. Ist der Multiplikator 2x wird jeder zweite Pixel übersprungen, was aber auch einen Leistungsvorteil mit sich bringt. Deswegen sollte man die Abtastrate direkt im Shader einstellen.

      Blur Shader Vertex:

      Quellcode

      1. attribute vec3 in_Position;
      2. attribute vec2 in_TextureCoord;
      3. varying vec2 v_vTexcoord;
      4. varying float v_vDepth;
      5. void main()
      6. {
      7. vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
      8. gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
      9. v_vTexcoord = in_TextureCoord;
      10. v_vDepth = gl_Position.z;
      11. }
      Alles anzeigen


      Blur Shader Fragment:

      Quellcode

      1. varying vec2 v_vTexcoord;
      2. uniform sampler2D tex_depth;
      3. uniform vec2 size;
      4. uniform vec2 blur_multiplikator;
      5. const int blur_size = 8;
      6. void main()
      7. {
      8. vec3 n_color;
      9. float divid = 0.;
      10. float divid_add = 0.;
      11. for(int ix = -blur_size; ix <= blur_size; ix++)
      12. for(int iy = -blur_size; iy <= blur_size; iy++)
      13. {
      14. divid_add = texture2D( tex_depth, v_vTexcoord + vec2(float(ix)/size.x,float(iy)/size.y) * texture2D(tex_depth, v_vTexcoord).r * blur_multiplikator).r;
      15. n_color += texture2D( gm_BaseTexture, v_vTexcoord + vec2(float(ix)/size.x,float(iy)/size.y) * texture2D(tex_depth, v_vTexcoord).r * blur_multiplikator).rgb*divid_add;
      16. divid += divid_add;
      17. }
      18. n_color /= divid;
      19. gl_FragColor = vec4(n_color, 1.);
      20. }
      Alles anzeigen


      Man sollte darauf Achten, dass bei der Erhöung von blur_size, die Leistung Quadratisch ansteigt.

      Screenshot:


      Projektdatei: simudox.de/tilt_shift_shader/tiefenschaerfe.gmz

      Alle Shader dürfen frei verändert werden. Ich übernehme aber keine Haftung für Code und Shader. Credit wird nicht verlangt, darf aber gerne gemacht werden. Ich stehe für jegliche Fragen zum Thema offen. Auf Nachfrage erweitere ich auch die Shader.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Chris987 ()