<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
		<id>https://wiki.delphigl.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Coolcat</id>
		<title>DGL Wiki - Benutzerbeiträge [de]</title>
		<link rel="self" type="application/atom+xml" href="https://wiki.delphigl.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Coolcat"/>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php/Spezial:Beitr%C3%A4ge/Coolcat"/>
		<updated>2026-04-17T13:24:22Z</updated>
		<subtitle>Benutzerbeiträge</subtitle>
		<generator>MediaWiki 1.27.4</generator>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Link&amp;diff=25343</id>
		<title>Link</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Link&amp;diff=25343"/>
				<updated>2011-08-13T20:12:06Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Texturen */ cgtextures.com&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!--&lt;br /&gt;
!!!!!!!! NUTZT DIESE TABELLE ALS VORLAGE FÜR WEITERE LINKTABELLEN !!!!!!!&lt;br /&gt;
&lt;br /&gt;
{|{{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[Link]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[Link]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Linksammlung soll allgemeine Links zu unterschiedlichen Themen bieten, die mit der Multimediaentwicklung zu tun haben.&lt;br /&gt;
{{Hinweis|Vielleicht ist eine Zusammenführung mit [[Tool]] sinnvoll!}}&lt;br /&gt;
{{Hinweis|Vielleicht ist eine Aufteilung in Unterseiten sinnvoll (möglicherweise mit Kategorien)}}&lt;br /&gt;
&lt;br /&gt;
= Entwicklung mit OpenGL =&lt;br /&gt;
&lt;br /&gt;
== Hardwareinfos ==&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://delphigl.de/glcapsviewer/listreports.php OpenGL Hardware Registry - Hardwareübersicht]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Eine Datenbank aller Grafikkarten die in der Harware Registry vorhanden sind. Die einzelnen Artikel enthalten Infos darüber, welche Extensions von der Grafikkarte unterstützt werden.&lt;br /&gt;
|-&lt;br /&gt;
|[http://delphi3d.net/hardware/allexts.php OpenGL Hardware Registry - Extensionübersicht]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Eine Datenbank aller Extensions die in der Harware Registry vorhanden sind. Die einzelnen Artikel enthalten Infos darüber, welche Grafikkarten die entsprechende Extension unterstützen.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Tutorials ==&lt;br /&gt;
&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
||[[Tutorial|DGL Wiki - Tutorial]]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|DelphiGL.com ist Betreiber dieses Wikis und stellt eine Vielzahl Tutorials mit Schwehrpunkt OpenGL zur Verfügung. &lt;br /&gt;
Neben Tutorials für Einsteiger und OpenGL Anfänger gibt es auch fortgeschrittene Themen wie Shader oder Partikelsysteme.Sprache der Wahl ist hier Delphi.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.joachimrohde.com/cms/xoops/modules/articles/index.php?cat_id=1 joachimrohde.com]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Auf dieser Seite findet ihr deutsche Übersetzungen der bekannten NeHe Tutorials. Sprache der Wahl ist hier C++.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.cg.tuwien.ac.at/studentwork/VRSem96/OpenGL/ Technische Universität Wien]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Diese Seite bietet einen allgemeinen Überblick über die primären Funktionen von OpenGL und richtet sich vor allem an Einsteiger.&lt;br /&gt;
|-&lt;br /&gt;
|[http://nehe.gamedev.net/ Nehe] &lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Die berühmt berüchtigten NeHe Tutorials sind eine umfangreiche Sammlung an Tutorials zum Thema OpenGL. Von Anfängertutorials bis hin zu komplexen Effekten wird alles behandelt. Sprache der Wahl ist hier C++. (Eine Übersetzung der NeHe Tutorials findet ihr bei Joachim Rhode (siehe oben). )&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.sulaco.co.za/tut.htm sulaco.co.za] &lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| OpenGL Beispiele in Delphi&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== OpenGL Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== GL ===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[[:Kategorie:GL|DGL Wiki - Kategorie GL]]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Wiki-Kategorie die alle übersetzten OpenGL Befehle enthält.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL.tar.gz OpenGL.org - Orginal Spezifkationen]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Die Orginalspezifikationen der OpenGL 1.0 und OpenGL 1.1 Befehle. (Diese HTML-Dokumente sind nicht Copy&amp;amp;Paste freundlich, da sie vermutlich automatisch aus vorhandenen Dateien (Postscript) erzeugt wurden.)&lt;br /&gt;
|-&lt;br /&gt;
|[http://developer.3dlabs.com/documents/glmanpage_index.htm 3dLabs]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Die Orginalspezifikationen der OpenGL 1.0 und OpenGL 1.1 Befehle.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.mevis.de/~uwe/opengl/opengl.html mevis.de]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Sammlung von Englischsprachigen Spezifikationen für GL(bis Version 1.1), GLX und GLU Funktionen.&lt;br /&gt;
|-&lt;br /&gt;
|[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/glfunc01_4f03.asp MSDN von Microsoft]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|OpenGL Dokumentation in der MSDN. Die Erklärung der MSDN sind meist keine reinen Kopien der Orginalspezifikationen, sondern enthalten hin und wieder auch zusätzliche Informationen, oder formulieren Texte auf verständlichere Art und Weise.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== GLU ===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[[:Kategorie:GLU|DGL Wiki - Kategorie GLU]]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Wiki-Kategorie die alle übersetzten GLU Befehle enthält.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/glu/ OpenGL.org - Orginal Spezifkationen]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Orginalspezifikationen für die GLU Befehle. (Diese HTML-Dokumente sind nicht Copy&amp;amp;Paste freundlich, da sie vermutlich automatisch aus vorhandenen Dateien (Postscript) erzeugt wurden.)&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.mevis.de/~uwe/opengl/opengl.html mevis.de]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Sammlung von englischsprachigen Spezifikationen für GL(bis Version 1.1), GLX und GLU Funktionen.&lt;br /&gt;
|-&lt;br /&gt;
|[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/glufnc01_0e43.asp MSDN von Microsoft]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|GLU Dokumentation in der MSDN. Die Erklärung der MSDN sind meist keine reinen Kopien der Orginalspezifikationen, sondern enthalten hin und wieder auch zusätzliche Informationen, oder formulieren Texte auf verständlichere Art und Weise.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== GLX ===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[[:Kategorie:GLX|DGL Wiki - Kategorie GLX]]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Wiki-Kategorie die alle übersetzten GLX Befehle enthält.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/glx/ OpenGL.org - Orginal  Spezifkationen]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Orgninalspezifikationen der GLX Befehle bei OpenGL.org.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.mevis.de/~uwe/opengl/opengl.html mevis.de]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Sammlung von Englischsprachigen Spezifikationen für GL(bis Version 1.1), GLX und GLU Funktionen.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== WGL ===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[[:Kategorie:WGL|DGL Wiki - Kategorie WGL]]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Wiki-Kategorie die alle übersetzten WGL Befehle enthält.&lt;br /&gt;
|-&lt;br /&gt;
|[http://developer.3dlabs.com/documents/wglmanpage_index.htm 3dLabs]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Sammlung einiger WGL Befehlsspezifikationen.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== GLUT ===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://freeglut.sourceforge.net/docs/api.php The freeglut Projekt]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Englischsprachige Dokumentation zum OpenGL Utility Toolkit kurz GLUT.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== SDL ===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[[:Kategorie:SDL|DGL Wiki - Kategorie SDL]]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Übersicht aller auf die SDL bezogenen Artikel hier im Wiki. Darunter befinden sich eine Vielzahl von Übersetzungen der SDL Befehlspezifikationen. Diese können auch in der [[SDL-Funktionsübersicht]] gefunden werden.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.libsdl.org/cgi/docwiki.cgi/SDL_20API libsdl.org]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Das Dokumentationswiki von libSDL.org enthält die komplette Dokumenation zur SDL in englischer Sprache.&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.fp.sdl.de.vu/ fp.sdl.de.vu]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| Eine Webseite die speziell Informationen über die Programmierung von SDL unter Freepascal zur Verfügung stellt.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== FAQs ==&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[[FAQ| DGL Wiki - FAQ]]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Ein FAQ (Frequently Asked Questions = Häufig gestellte Fragen) zu DelphiGL.com und dem DGL Wiki.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.3dsource.de/faq/index.htm 3dsource.de]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Ein FAQ zu Fragen rund um OpenGL.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Effekte und Techniken mit OpenGL ==&lt;br /&gt;
&lt;br /&gt;
===Glow===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.gamasutra.com/view/feature/2107/realtime_glow.php Gamasutra]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Beschreibt, wie man mit Hilfe von Shader(hier DirectX Shader) den Gloweffekt in Realtime umsetzt. Dabei wird erst die Technik dahinter erklährt, und dann wie man sie Schritt für Schritt umsetzt. &lt;br /&gt;
|-&lt;br /&gt;
|[http://collective.valve-erc.com/index.php?go=tron1 valve-erc.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Schritt für Schritt Anleitung, wie man mit Hilfe von Cg in OpenGL den Glow Effekt realisieren kann &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===HDR===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.gamedev.net/columns/hardcore/hdrrendering/ gamedev.net]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Hier wird sehr kurz eingeleitet, was HDR Rendering überhaupt ist, dann wird der Aufbau des *.hdr Formats erläutert, und dann geht es schon von 0 auf 100 zum Programmieren. Erklährungen gibt es kaum, dafür werden fertige Shader(für DirectX) geboten. &lt;br /&gt;
|-&lt;br /&gt;
|[http://www.gamedev.net/reference/articles/article2208.asp gamedev.net]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Beschreibt auch nur etwas kurz das HDR Verfahren, jedoch mit mehr mathematischen Hintergrund in Sachen Tonemapping. Beispielcode für Tonemapping in C++ liegt bei. &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BSP Bäume===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.3dtechdev.com/tutorials/leafbsp/3dbsptrees.html 3dtechdev.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Das beste Dokument überhaupt, für allem die selber BSP Bäume compilieren möchte. Ist belegt mit sehr viel Grafik. Erst beginnt er das Prinzip allgemein zum umschreiben, dann wird alles genau erläutert, und mit verständlichen Pseudocode untermalt. &lt;br /&gt;
|-&lt;br /&gt;
|[http://www.3dtechdev.com/tutorials/illegalgeometry/illegalgeometrytut.html 3dtechdev.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Nicht ganz so gut geworden, wie sein erstes Tutorial, aber erklährt auch sehr gut, wie man illegale Geometrie aufspürt und beseitigt. Wieder mit Grafiken und Pseudocodes verständlich belegt. Hiermit kann man seinem Map-Compiler den letzten Schliff verleien. &lt;br /&gt;
|-&lt;br /&gt;
|[http://www.gamedev.net/reference/articles/article981.asp Gamedev]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Von Michael Abrash, dürfte der ''Erfinder'' von Quake sein. Naja, meine Englischkenntnisse reichen hier nicht ganz aus, um den Text zu verstehen. Ist etwas umständlich geschrieben. Dafür mit Grafiken unterlegt. &lt;br /&gt;
|-&lt;br /&gt;
|[http://www.zfx-online.de/Tutorials.php?ID=11 Part 1 - Allgemein]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Einzigste deutsche Referenz zu BSP Bäumen, dafür Daumen hoch. Ist aber nicht das Wahre: schlechter C Code, und ich hatte auch nicht den Einduck, das er die Theorie zu diesem Thema bis in die Tiefe verstanden hat(oder er wollte es nicht zu akademisch Erklähren oO). Aber ein guter Einstieg in dieses Thema. &lt;br /&gt;
|-&lt;br /&gt;
|[http://www.zfx-online.de/Tutorials.php?ID=13 Part 2 - Kollision und PVS]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.cs.uwec.edu/~stevende/cs455/programs/GameTutorials%20-%20Quake%203%20BSP%20Format.htm Quake3 *.bsp Format]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Die beste Dokumentation für das BSP Format, die ich gefunden habe, besser als den Quake3 Sourcecode zu durchforsten. Leider etwas magere Eklährung zu wichtigen Themen wie Patches(Curved Surfaces). Ich pers. hatte jetzt 1 1/2 Wochen gebraucht, um das *.bsp Format vollständig zu verstehen. Ist aber anscheinend die beste Format Dokumentation im Netz. &lt;br /&gt;
|-&lt;br /&gt;
|[http://www.flipcode.com/articles/article_q2bsp.shtml Quake2 *.bsp Format]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Kann auch nur soviel sagen, wie beim Link zuvor. Es fehlen hier auch zum Teil Informationen. Man muss halt viel ausprobieren, und sich den Quake 2 Source zu Herzen nehmen, damit man es versteht. &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Himmel &amp;amp; Wolken===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.gamedev.net/community/forums/topic.asp?topic_id=86024 Gamedev]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Guter Thread mit vielen Anregungen und Links. &lt;br /&gt;
|-&lt;br /&gt;
|[http://www.gamedev.net/community/forums/topic.asp?topic_id=135654 Gamedev]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Hier wird die Diskussion zum obigen Link weitergeführt. &lt;br /&gt;
|-&lt;br /&gt;
|[http://freespace.virgin.net/hugo.elias/models/m_clouds.htm Plasma]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Eine nette Idee um Plasma in Echtzeit zu generieren, und zu verändern. Leider nichts zum Shading. &lt;br /&gt;
|-&lt;br /&gt;
|[http://nis-lab.is.s.u-tokyo.ac.jp/~nis/cdrom/sig00_cloud.pdf 3D Wolken]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Paper über generieren und rendern von 3 dimensionalen Wolken inklusive Schatten und Lichtstrahlen. Ich hab nur den Lichtstahlen-Algorithmus ausprobiert. Saulangsam aber sehr einfach zu implementieren. Einen Blick ist es auf jeden Fall wert. &lt;br /&gt;
|-&lt;br /&gt;
|[http://www.geocities.com/ngdash/whitepapers/skydomecolor.html Skydome]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Wenn man mal schnell gute Farben für seinen Skydome braucht... &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Terrain===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.flipcode.com/articles/article_geomipmaps.shtml FlipCode]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Artikel zu Geomipmaping. Zummindest hab ich es hier zum ersten mal gesehen. Und es funzt ganz gut. &lt;br /&gt;
|-&lt;br /&gt;
|[http://www.vterrain.org Virtual Terrain Project]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Eine Seite mit Links und Artikeln zu allem, was etwas mit Terrainrendering zu tun hat.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Sonstiges===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[[Materialsammlung]]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Sammlung von gängigen Parametern für [[glMaterial]] hier im Wiki.&lt;br /&gt;
|-&lt;br /&gt;
|[http://freespace.virgin.net/hugo.elias/radiosity/radiosity.htm Radiosity]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Erklährt sehr schön die Funktionsweise von Radiosity, liefert Beispielcodes und ist recht gut illustriert.&lt;br /&gt;
|-&lt;br /&gt;
|[http://legion.gibbering.net/projectx/paper/shadow%20mapping/ Shadowmapping]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Beschreibt das Trapezoidal Shadow Mapping - Verfahren für gerichte Lichter.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.flipcode.com/articles/article_generatingnames.shtml Namensgenerator]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Per Zufallsgenrator Namen erzeugen, und nie wieder kreativ werden müssen.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.paulsprojects.net/tutorials/simplebump/simplebump.html Bumpmapping]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Bumpmapping auf Lowend-Grakas. Damals war Bumpmapping noch was ganz tolles... &lt;br /&gt;
|-&lt;br /&gt;
|[http://www.paulsprojects.net/opengl/dpreflect/dpreflect.html Dot Product Reflect]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|zwar nur ne Demo, aber wenn man schon immer mal mit EMBM das Wasser aus Morrowind nachproggen wollte... &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Beispiele/Demos mit Quelltext ==&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.delphigl.de DelphiGL.de]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Hier finden sich besonders eindrucksvolle Newton Physik Demos. Meißtens sogar mit Quelltext.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.shadow3d.de.vu/ Shadow 3D]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Hier finden sich zwei Demos wie man Quake 3 Modelle lädt und eine glSlang Demo&lt;br /&gt;
|-&lt;br /&gt;
|[[Shadersammlung]] im DGL Wiki&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Hier findet man freie Shadersourcen.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.humus.name Humus]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| Diverse eindrucksvolle Demos zu verschiedenen Techniken, häufig mit Quelltext in C (teilweise OpenGL / DirectX)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== weitere Links ==&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www-user.tu-chemnitz.de/~pester/Lehre/CompGeo.pdf CompGeo.pdf]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Script &amp;quot;Einführung in die Computergeometrie&amp;quot; von Dr. Pester (TU-Chemnitz). Ideales Nachschlagewerk für die Mathematik die einem bei der Grafikprogrammierung so begegnet.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.robsite.de/ robsite.de]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Auf dieser riesigen Seite finden sich auch viele weitere Links über verschiedene Themen der 3D Programmierung.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.3dlinks.com/links.cfm?categoryid=3&amp;amp;subcategoryid=21 3dlinks.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| Diese Seite hat es sich zur Hauptaufgabe gemacht Links auf Seiten mit dem Thema 3D zu sammeln.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.two-kings.de/links.html two-kings.de]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| Sammlung diverser interessanter Links, vorallem auf Themen im gamedev-Forum verweisend.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.delgine.com Delgine.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| Heimat des freien 3D Modellers &amp;quot;DeleD&amp;quot;, welcher in Delphi programmiert ist. Ihr findet dort auch Modellpackete, Texturpackete, Plugins und Hilfe bei der Programmierung von Delphi und OpenGL.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Spieleentwicklung =&lt;br /&gt;
&lt;br /&gt;
==Allgemein==&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.spieleprogrammierung.net/ Spieleprogrammierung - OpenGL - GLSL - OpenAL - KI - Animation - Spielephysik]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Auf dieser Seite findet Ihr Artikel, Tutorials und Programmbeispiele rund um das Thema Spieleprogrammierung. Programmiersprache ist hier C/C++.&lt;br /&gt;
|-&lt;br /&gt;
|[http://graphics-and-physics-framework.spieleprogrammierung.net/ OpenGL Graphics And Physics Framework]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Auf dieser Seite findet Ihr ein frei verfügbares OpenGL basiertes Grafik und Physik Framework (LGPL-Lizenz) samt zugehöriger Probammbeispiele. Programmiersprache ist hier C/C++.&lt;br /&gt;
|-&lt;br /&gt;
|[http://audio-framework.spieleprogrammierung.net/ OpenAL Audio Framework]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Auf dieser Seite findet Ihr ein frei verfügbares OpenAL Audio Framework (LGPL-Lizenz) samt zugehöriger Probammbeispiele. Unterstützt werden 3D Sounds, EFX (EAX Alternative) Reverb Effekte sowie Audio Streaming (Music and Voices). Programmiersprache ist hier C/C++.&lt;br /&gt;
|-&lt;br /&gt;
|[http://spieleentwicklung-im-web.spieleprogrammierung.net/ Spieleentwicklung im Web]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Auf dieser Seite findet Ihr Links (E-Books, Präsentationen, wissenschaftliche Arbeiten und Tutorials) zu verschiedenen Themengebieten der Spieleentwicklung.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Kopierschutz==&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://inner-smile.com/nocrack.phtml Inner-Smile.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| Seite mit Gedanken zum Thema &amp;quot;Wie schütze ich meine Programme vor Crackern&amp;quot;. Falls die Seite nicht erreichbar ist hat Google noch ne [http://www.google.com/search?q=cache:hpqPT5G4WB0J:www.inner-smile.com/nocrack.phtml+http://inner-smile.com/nocrack.phtml&amp;amp;hl=de&amp;amp;gl=de&amp;amp;ct=clnk&amp;amp;cd=1 Version im Archiv].&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Künstliche Intelligenz ==&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.kbs.uni-hannover.de/Lehre/KI2/Presentationen/presentation9798/esprit/spieltheorie/spieltheorie.html Uni-Hannover - Spieltheorie]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Artikel über die Grundlagen von KI in Spielen.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.robsite.de/tutorials.php?tut=ki Robsite KI-Tutorials]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Sammlung von Texten zum Thema KI in Spielen. Beispiele sind meist in C++.&lt;br /&gt;
|-&lt;br /&gt;
|[http://ai-depot.com/ AI-Depot.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| Seite die sich ausschließlich mit KI in Spielen beschäftigt.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.ai-junkie.com/links.html AI-Junkie.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Linkseite mit vielen KI relevanten Links. Die Seite AI-Junie selbst behandelt auch viele Themen rund um KI.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.aiwisdom.com/ AIWisdom.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Seite die sich mit KIs in Spielen beschäftigt und sich zum Ziel gesetzt hat, alle Artikel zum Thema zu finden und zu Katalogisieren. Es gibt hier auch Artikel über die KIs bestehender Spiele.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.aiguru.com/ AIGuru.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Newsseite zum Thema KI, Künstliches Leben, Hiernforschung, Robotik, Nanotechnologie - Halt alles was Spass macht.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.generation5.org/ Generation5.org]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Seite über KI, Robotik, etc. . Leider scheint die Seite Probleme zu haben regelmäßig aktualisiert zu werden. Aber zumindest sollte man einige Artikel finden. Die werden ja nicht schlecht.&lt;br /&gt;
|-&lt;br /&gt;
|www.gameai.com&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|''Wurde Januar 2010 als down gemeldet. Weiss irgendwer etwas genaueres?'' Seite zum Thema KI in Spielen. Soll wohl einen gewissen Stellenwert in der AI-Szene haben.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www-i1.informatik.rwth-aachen.de/~algorithmus/algo19.php Algorithmus der Woche - Informatikjahr 2006]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Vielleicht schon etwas älter aber doch eine gute Anleitung um eine einfache Schach-KI zu erstellen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Ressourcen =&lt;br /&gt;
In dieser Gruppe finden sich Links um die Ressourcen für Eure Projekte zu erstellen.&lt;br /&gt;
&lt;br /&gt;
== Texturen / 2D-Grafiken ==&lt;br /&gt;
&lt;br /&gt;
=== Texturen ===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;20%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;10%&amp;quot;|Genre&lt;br /&gt;
!width=&amp;quot;10%&amp;quot;|Lizenz&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|GPL&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|MPL&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|OSS&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|CSS&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Komm.&lt;br /&gt;
!width=&amp;quot;35%&amp;quot;|Bemerkungen&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.davegh.com/vg/home.htm davegh.com]&lt;br /&gt;
|Fantasy&lt;br /&gt;
|[http://www.davegh.com/vg/termsandconditions.htm Eigene]&lt;br /&gt;
|?&lt;br /&gt;
|?&lt;br /&gt;
|✔&lt;br /&gt;
|✔&lt;br /&gt;
|✘&lt;br /&gt;
|Die Lizenz vererbt sich in die Projekte hinein.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.cgtextures.com/ cgtextures.com]&lt;br /&gt;
|Allgemein&lt;br /&gt;
|[http://www.cgtextures.com/content.php?action=license Eigene]&lt;br /&gt;
|✘&lt;br /&gt;
|✘&lt;br /&gt;
|✘&lt;br /&gt;
|?&lt;br /&gt;
|?&lt;br /&gt;
|Die Lizenz will im wesentlichen verhindern das Texturen in anderen Textursammlungen auftauchen. So wie ich das verstehe ist es kein Problem die Texturen unter einer anderen Lizenz zu veröffentlichen als die eigentliche Software. Die Software kann also sehr wohl OpenSource sein. Im Zweifelsfalls einfach den Support anschreiben, der ist sehr nett.&lt;br /&gt;
|-&lt;br /&gt;
|url&lt;br /&gt;
|Futuristisch, Mittelalter, etc.&lt;br /&gt;
|CC-BY-SA, etc., gerne auch mit Direktlink auf die Lizenzseite, so vorhanden.&lt;br /&gt;
|✔/✘/?&lt;br /&gt;
|✔/✘/?&lt;br /&gt;
|✔/✘/?&lt;br /&gt;
|✔/✘/?&lt;br /&gt;
|✔/✘/?&lt;br /&gt;
|Alle Bilder sind von Käse&lt;br /&gt;
|}&lt;br /&gt;
'''Legende:'''&lt;br /&gt;
:'''Kostenpflichtige Seiten''' sind mit einem * zu '''kennzeichnen'''&lt;br /&gt;
:GPL: Kompatibel mit der GPL&lt;br /&gt;
:MPL: Kompatibel mit der MPL&lt;br /&gt;
:OSS: Verwendung in OpenSource Software nicht verboten&lt;br /&gt;
:CSS: Verwendung in ClosedSource Software nicht verboten&lt;br /&gt;
:Komm: Verwendung in kommerzieller Software nicht verboten&lt;br /&gt;
&lt;br /&gt;
=== Texturerstellung ===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://3d.diehlsworld.de/textures/index.htm 3d.diehlsworld.de]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Auf dieser Seite findet ihr kurze aber inhaltlich reiche Tutorials zu der Frage &amp;quot;Wie erstelle ich XXXXX-Texturen&amp;quot;. Dabei reicht die Palette von Untergrundtexturen wie Gras, Sand , Fels und Wasser (und Kombinationen dieser) bis hin zu Blitzen und Partikeln. Als Programm wird hier Photoshop verwendet.&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.gimps.de/gimp/textur-muster/ gimps.de]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Schritt für Schritt Anleitungen wie man mit Gimp verschiedene Texturen erstellt.&lt;br /&gt;
|-&lt;br /&gt;
|[http://cbt.k090999.de/gimp.php#texturen k090999.de]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Gimptutorials zum Erstellen verschiedene Texturen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Modelle / 3D-Grafiken ==&lt;br /&gt;
&lt;br /&gt;
=== Blender ===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://blendpolis.de/f/viewtopic.php?t=5786 Blendpolis]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Ein Thread im Forum von [http://blendpolis.de Blendpolis] in dem '''sehr''' viele Tutorials aufgelistet sind&lt;br /&gt;
|-&lt;br /&gt;
|[http://blendpolis.de/f/article_cat.php?fldAuto=7 Blendpolis]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
| Eigentliche Tutorial Seite von [http://blendpolis.de Blendpolis] die Auswahl ist hier aber nicht so groß&lt;br /&gt;
|-&lt;br /&gt;
|[http://wikivid.com/index.php/Blender Blender at wikivid.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| Linksammlung von Blender Video Tutorials&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.blender.org/education-help/tutorials/getting-started/ blender3d.org]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| Auch die Heimatseite von Blender bietet viele Tutorials unter anderm auch Video Tuorials&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.Blenderunderground.com Blenderunderground]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
| '''sehr gute''' Video-Tutorials&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Sounds ==&lt;br /&gt;
&lt;br /&gt;
===Musik===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.jamendo.com/ Jamendo.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Viele Künstler stellen hier ihre Alben unter einer Creative Commons Lizenz online. Man kann dort die Künstler auch kontaktieren und fragen ob man ihre Musik nutzen kann. Taugt außerdem als Radio! ;)&lt;br /&gt;
|-&lt;br /&gt;
|[http://incompetech.com/m/c/royalty-free/ Incompetech.com]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Viele sehr schön gemacht instrumentale Musik aus allen möglichen Genres. Sie ist frei verwenderbar, wenn man den Macher der Musik (Kevin MacLeod) in seinem Programm erwähnt. Aber das hat er auch verdient. Folgende Programme nutzen beispielsweise (auch) Musik von incompetech.com:&lt;br /&gt;
* OpenParty&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Soundeffekte===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.freesound.org/ freesound.org]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|Diverse Sounds unter [http://www.creativecommons.org/ CreativeCommons] Lizenzen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Soundprogramme===&lt;br /&gt;
{| width = &amp;quot;100%&amp;quot; {{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;25%&amp;quot;|Link&lt;br /&gt;
!width=&amp;quot;5%&amp;quot;|Sprache&lt;br /&gt;
!width=&amp;quot;70%&amp;quot;|Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|[http://audacity.sourceforge.net/?lang=de Sourceforgeseite von Audacity]&lt;br /&gt;
|{{Deutsch}}&lt;br /&gt;
|Ein einfaches, freies Programm um Sounddateien zu bearbeiten. Man kann es auch nutzen um Soundeffekte neu zu erstellen, indem man z.B. ein Geräusch mit dem Mikrofon aufnimmt und einige Effekt anwendet, um es passender und nicht mehr aufgenommen klingen zu lassen.&lt;br /&gt;
|-&lt;br /&gt;
|[http://lmms.sourceforge.net/ LMMS]&lt;br /&gt;
|{{Englisch}}&lt;br /&gt;
|LMMS (Linux MultiMedia Studio) ist ein OpenSource+Freeware Composer-Programm ähnlich zum bekannteren aber auch teuren CuBase(r). Trotz des Namens auch für Windows erhältlich.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=glClearDepth&amp;diff=25091</id>
		<title>glClearDepth</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=glClearDepth&amp;diff=25091"/>
				<updated>2011-05-09T15:31:38Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Parameter */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= glClearDepth =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Name ==&lt;br /&gt;
'''glClearDepth''' - legt den Wert fest mit dem der Tiefenpuffer beim löschen des selbigen gefüllt wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Delphi-Spezifikation ==&lt;br /&gt;
procedure glClearDepth(depth: TGLclampd); stdcall;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameter ==&lt;br /&gt;
&amp;lt;table border=1 rules=all&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;''depth''&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;Ist der Wert, mit dem beim löschen der Tiefenpuffer gefüllt wird. Voreingestellt ist 1, der größtmögliche Tiefenwert.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beschreibung == &lt;br /&gt;
Mit glClearDepth können Sie den Wert festlegen mit dem der [[Tiefenpuffer]] beim Löschen des Puffers (mittels [[glClear]]) überschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Hinweise ==&lt;br /&gt;
Der übergebene Wert (depth) wird auf das Interval [0,1] begrenzt. Der Wert 0 ist der kleinstmögliche Tiefenwert, 1 der größtmögliche.&lt;br /&gt;
&lt;br /&gt;
== Fehlermeldungen ==&lt;br /&gt;
GL_INVALID_OPERATION wird generiert wenn glClearDepth innerhalb eines [[glBegin]]-[[glEnd]] Blocks aufgerufen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==  Zugehörige Wertrückgaben ==&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_DEPTH_CLEAR_VALUE|GL_DEPTH_CLEAR_VALUE]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
(andere Löschfunktionen)&lt;br /&gt;
[[glClear]], [[glClearAccum]], [[glClearColor]], [[glClearIndex]], [[glClearStencil]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:GL|ClearDepth]]&lt;br /&gt;
 [[Kategorie:GL1.0]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=glClearDepth&amp;diff=25090</id>
		<title>glClearDepth</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=glClearDepth&amp;diff=25090"/>
				<updated>2011-05-09T15:30:27Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Hinweise */ Umgerechnet ist hier missverständlich.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= glClearDepth =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Name ==&lt;br /&gt;
'''glClearDepth''' - legt den Wert fest mit dem der Tiefenpuffer beim löschen des selbigen gefüllt wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Delphi-Spezifikation ==&lt;br /&gt;
procedure glClearDepth(depth: TGLclampd); stdcall;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameter ==&lt;br /&gt;
&amp;lt;table border=1 rules=all&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;''depth''&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;td&amp;gt;Ist der Wert, mit dem beim löschen der Tiefenpuffer gefüllt wird.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beschreibung == &lt;br /&gt;
Mit glClearDepth können Sie den Wert festlegen mit dem der [[Tiefenpuffer]] beim Löschen des Puffers (mittels [[glClear]]) überschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Hinweise ==&lt;br /&gt;
Der übergebene Wert (depth) wird auf das Interval [0,1] begrenzt. Der Wert 0 ist der kleinstmögliche Tiefenwert, 1 der größtmögliche.&lt;br /&gt;
&lt;br /&gt;
== Fehlermeldungen ==&lt;br /&gt;
GL_INVALID_OPERATION wird generiert wenn glClearDepth innerhalb eines [[glBegin]]-[[glEnd]] Blocks aufgerufen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==  Zugehörige Wertrückgaben ==&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_DEPTH_CLEAR_VALUE|GL_DEPTH_CLEAR_VALUE]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
(andere Löschfunktionen)&lt;br /&gt;
[[glClear]], [[glClearAccum]], [[glClearColor]], [[glClearIndex]], [[glClearStencil]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:GL|ClearDepth]]&lt;br /&gt;
 [[Kategorie:GL1.0]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Vertexbufferobject&amp;diff=25087</id>
		<title>Tutorial Vertexbufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Vertexbufferobject&amp;diff=25087"/>
				<updated>2011-05-04T08:30:10Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Ave! */ Zeitgemäßge Überarbeitung&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Ave!==&lt;br /&gt;
&lt;br /&gt;
Und willkommen zu meinem zweiten Tutorial. Diesmal beschäftigen wir uns mit den sogenannten ''Vertex Buffer Objects''. Die Extension '''GL_ARB_vertex_buffer_object''' wurde vom ARB am 12.Februar 2003 fertiggestellt. Später im Jahr 2003 wurde sie in den Core von OpenGL 1.5 aufgenommen. In diesem Tutorial werden diese Core-Funktionen verwendet. Das Verhalten der Extension sollte aber identisch sein.&lt;br /&gt;
&lt;br /&gt;
VBOs (Vertex Buffer Objects) sind quasi eine Erweiterung der Vertexarrays, die jedoch den großen Vorteil besitzen, dass ihre Vertexdaten serverseitig, also im schnellen VRAM der Grafikkarte, statt wie bei den VAs im Hauptspeicher, abgelegt werden. Das ist bei einer Displayliste zwar (fast) genauso der Fall, allerdings können die Daten nicht so einfach dynamisch geändert werden, was bei einem VBO aber bei Bedarf sehr einfach ist.&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt vereint das VBO also die Flexibilität eines Vertexarrays mit der Geschwindigkeit einer Displayliste und eignet sich daher besonders für das Rendern aufwändiger Geometrie, egal ob statisch oder dynamisch.&lt;br /&gt;
&lt;br /&gt;
Eine weitere tolle Neuerung der VBO-API ist die Tatsache, dass man sich von OpenGL einen Pointer übergeben lassen kann, mit dessen Hilfe man Vertexdaten direkt in den VRAM schreiben kann. Dadurch spart man sich natürlich den Umweg über den Hauptspeicher und somit auch den erhöhten Speicherbedarf des Programmes beim Laden von Modellen o.&amp;amp;nbsp;Ä.&lt;br /&gt;
&lt;br /&gt;
VBOs bieten übrigens auch die Möglichkeit, sie je nach Anwendungsfall vom Grafikkartentreiber &amp;quot;optimieren&amp;quot; zu lassen. So legt man Vertexdaten, die im Nachhinein nicht mehr verändert werden sollenm am besten mit dem Schlüsselwort ''STATIC_DRAW'' ab, während dynamische Vertexdaten über ''DYNAMIC_DRAW'' abgelegt werden sollten. Doch dazu gibts später nähere Informationen.&lt;br /&gt;
&lt;br /&gt;
==Hardwareunterstützung==&lt;br /&gt;
&lt;br /&gt;
Inzwischen sollten alle gängigen Grafikkarten diese Extension problemlos unterstützen, ältere Modelle tun dies aber evtl. nur emuliert über den Treiber. Will man sein Programm auch für sehr alte Hardware (Karten älter als ATIs Radeon 9x00 bzw. NVidias GeForce 4-Serie) lauffähig machen, sollte man noch einen alternativen Renderpfad auf Basis von Displaylisten einbauen.&lt;br /&gt;
&lt;br /&gt;
==Erstellen des Vertexbufferobjects==&lt;br /&gt;
&lt;br /&gt;
Wie der Name vermuten lässt, handelt es sich beim VBO um ein Objekt, welches in Sachen Syntax an die anderen in OpenGL verfügbaren Objekte wie z.&amp;amp;nbsp;B. Texturenobjekte angelehnt ist. Das Erstellen eines VBOs geht daher genauso einfach von der Hand:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glGenBuffers(1, @VBO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun sollten wir in VBO eine gültige ID zurückgeliefert bekommen haben, welche ein Vertex buffer object referenziert.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun ein VBO erstellt haben, sollten wir dieses natürlich auch &amp;quot;aktivieren&amp;quot; (also binden), damit wir mit ihm arbeiten können:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBindBuffer(GL_ARRAY_BUFFER, VBO);&lt;br /&gt;
glEnableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie erwähnt binden wir in der ersten Zeile unser frisch erstelltes VBO, während wir in der zweiten Zeile Vertexarrays auf der Clientseite aktivieren. Dies ist deshalb nötig, da wir das VBO später genauso zeichnen werden wie dies bei einem normalen VA der Fall wäre.&lt;br /&gt;
&lt;br /&gt;
==Übergabe der Vertexdaten (Variante 1: Ohne Umweg über den Hauptspeicher)==&lt;br /&gt;
&lt;br /&gt;
Wie in der Einleitung bereits erwähnt, bietet uns die VBO-API die Möglichkeit, unsere Vertexdaten ohne Umweg über den Hauptspeicher direkt in den Grafikkartenspeicher zu schreiben. Da dies eines der besten Features dieser neuen Extension ist, wollen wir uns auch zuerst mit dieser Übergabemethode beschäftigen.&lt;br /&gt;
&lt;br /&gt;
''Allerdings erstmal eine kleine Warnung vorweg: Pointer sind ein recht komplexes Thema, und ihre Nutzung erfordert einige Kenntnisse. Wer weder weiß, wie man damit umgeht, noch wie man sie einsetzt, sollte sich da zuerstmal schlau machen. Denn mit Pointer kann man ganz böse Sachen machen, wenn man sie nicht richtig nutzen kann!''&lt;br /&gt;
&lt;br /&gt;
Um unsere Vertexdaten als Pointer zu nutzen,müssen wir erstmal eine Vertexstruktur erstellen und auch einen passenden Pointertyp. Der Pointertyp ist zwar nicht zwingend (ein ^ täte es auch), aber macht die Sache übersichtlicher:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
PVertex = ^TVertex;&lt;br /&gt;
TVertex = packed record&lt;br /&gt;
 S,T,X,Y,Z : TGLFloat;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sicher fragen sich einige von euch jetzt, warum unser TVertex gerade so aussieht (also zuerst S,T und dann der Rest). Das liegt daran, dass wir unser VBO später über die Funktion [[glInterleavedArrays]] rendern werden, welche wissen will, wie unsere Vertexdaten im Speicher abgelegt sind. Unsere Vertexstruktur entspricht dabei dem Format ''GL_T2F_V3F''. ''T2F'' entspricht 2 Floats für die Texturenkoordinaten (S&amp;amp;T), gefolgt von ''V3F'' für die drei Floatwerte die unsere Vertexkoordinaten repräsentieren. Wenn ihr ein anderes Vertexformat verwendet, müsst ihr euch auch ein passendes Vertexformat aus der Spezifikation heraussuchen.&lt;br /&gt;
Ich verwende oben übrigens einen '''packed Record''' (auch wenn dies bei unserem Vertexformat nicht unbedingt ein Muss ist). Dies tue ich deshalb, da Delphi Werte bei einem normalen Record für einen beschleunigten Zugriff an einem (Double)Word-Raster ausrichtet und dies deshalb bei bestimmten Vertexformaten Probleme bereiten könnte.&lt;br /&gt;
&lt;br /&gt;
Hoffe mal das euch obiges klar ist,denn jetzt gehts ans Eingemachte. Wir kommen zum &amp;quot;schwersten&amp;quot; (ist ja ein relativer Begriff) Teil des Tutorials, nämlich der Übergabe der Vertexdaten an das VBO. Dazu müssen wir OpenGL zuerstmal mitteilen, wie groß unser VBO eigentlich sein soll,damit wir auch genug Platz im VRAM bekommen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBufferData(GL_ARRAY_BUFFER, VertexAnzahl*SizeOf(TVertex), nil, GL_STATIC_DRAW);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schauen wir uns also mal die Parameter von [[glBufferData]] an. Zuerst müssen wir OpenGL mitteilen, was für einen Puffer wir erstellen wollen (in zukünftigen Versionen werden wohl noch andere folgen, z.&amp;amp;nbsp;B. Überbuffer). In unserem Falle also einen Arraypuffer. Parameter zwei übergibt dann den Speicherplatz, den wir reservieren wollen und ist recht selbsterklärend. Der dritte Parameter würde normalerweise einen Pointer auf unsere Vertexdaten enthalten. Da wir diese aber nicht im Hauptspeicher abgelegt haben, sondern über einen Pointer dort noch ablegen wollen, übergeben wir hier '''nil'''. OpenGL nimmt dies zur Kenntnis und spuckt dementsprechend auch keinen Fehler aus.&lt;br /&gt;
&lt;br /&gt;
Den letzten Parameter hab ich auch schon kurz in der Einführung angesprochen. Er teilt OpenGL mit, auf welchen Anwendungsfall unser VBO optimiert werden soll. Dabei gibt es folgende Anwendungsfälle,die ihr euch dann entsprechend eurer VBO-Verwendung aussuchen könnt:&lt;br /&gt;
&lt;br /&gt;
{|rules=&amp;quot;all&amp;quot; border = &amp;quot;1&amp;quot;&lt;br /&gt;
! STREAM_DRAW&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann eher selten als Quelle für OpenGL-Befehle genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STREAM_READ&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann selten von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! STREAM_COPY&lt;br /&gt;
| Die Vertexdaten werden einmal durch Kopieren von der GL übergeben und dann eher selten als Quelle für OpenGL-Befehle genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_DRAW&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_READ&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann recht oft von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_COPY&lt;br /&gt;
| Die Vertexdaten werden einmal durch Kopieren von der GL übergeben und dann recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_DRAW&lt;br /&gt;
| Die Vertexdaten werden wiederholt angegeben (verändert) und recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_READ&lt;br /&gt;
| Die Vertexdaten werden wiederholt durch das Auslesen von Daten durch OpenGL angegeben (verändert) und oft von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_COPY&lt;br /&gt;
| Die Vertexdaten werden wiederholt durch das Auslesen von Daten durch OpenGL angegeben (verändert) und recht oft zum Rendern genutzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beachtet werden sollte, dass es sich bei obigen Anwendungsmuster nur um Hinweise (Hints) handelt, die es auch in anderen Bereichen von OpenGL gibt. Diese Hinweise sind allerdings nicht verbindlich und werden von Treiber zu Treiber unterschiedlich behandelt.&lt;br /&gt;
&lt;br /&gt;
Nachdem OpenGL nun weiss was für ein VBO wir genau haben wollen, brauchen wir letztendlich nur noch einen Pointer, der &amp;quot;auf&amp;quot; das VBO zeigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
VBOPointer := glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion [[glMapBuffer]] liefert uns einen Pointer zurück, der den Speicherplatz des VBOs in den Adressraum des Clients &amp;quot;ummappt&amp;quot;, also in den Hauptspeicher. Dadurch wird es dann möglich, über eine Adresse im Hauptspeicher des Rechners direkt in den Grafikkartenspeicher zu schreiben. Der zweite Parameter gibt übrigens an, das wir in das VBO schreiben wollen. Der einzige weitere zulässige Parameter ist hier '''GL_READ_ONLY''', welcher zum Auslesen des VBOs dient.&lt;br /&gt;
&lt;br /&gt;
Endlich haben wir einen Pointer auf unser inzwischen auch im VRAM der Grafikkarte richtig dimensioniertes Vertexbufferobject, und sind nun in der Lage dort unsere Vertexdaten abzulegen. Was jetzt folgt ist ein wenig Pseudo-Code der diesen Vorgang darstellt. Pseudo-Code deshalb, weil die Vertexdaten die ins VBO kommen ja in jeder Anwendung unterschiedlich sind, und man so auch selbst ein wenig denken muss:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
VertexDatenLaenge := 0;&lt;br /&gt;
for i := 0 to High(MeineVertexDaten) do&lt;br /&gt;
 begin&lt;br /&gt;
 VertexPointer    := VBOPointer;&lt;br /&gt;
&lt;br /&gt;
 VertexPointer^.X := GeneriertesVertex.X;&lt;br /&gt;
 VertexPointer^.Y := GeneriertesVertex.Y;&lt;br /&gt;
 VertexPointer^.Z := GeneriertesVertex.Z;&lt;br /&gt;
 VertexPointer^.S := GeneriertesVertex.S;&lt;br /&gt;
 VertexPointer^.T := GeneriertesVertex.T;&lt;br /&gt;
&lt;br /&gt;
 inc(Integer(VBOPointer), SizeOf(TVertex));&lt;br /&gt;
&lt;br /&gt;
 inc(VertexDatenLaenge);&lt;br /&gt;
 end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da sich dieses Tutorial (und v.&amp;amp;nbsp;a. diese Methode der Vertexdatenübergabe) an die fortgeschritteneren wendet, rede ich nicht lange um den heissen Brei herum. Wir durchlaufen also eine Schleife, in der wir unsere Vertexdaten dynamisch erstellen. Entweder in jedem Durchlauf, oder wir laden die Daten vor der Schleife z.&amp;amp;nbsp;B. aus einem 3D-Modell und entfernen sie danach aus dem Hauptspeicher. Erste Methode spart sowohl beim Programmstart, also auch während des Programmlaufes Arbeitsspeicher, während letztere dies nur während des Programmablaufs macht.&lt;br /&gt;
&lt;br /&gt;
Wir setzen die Adresse des Pointers der auf das momentan zu verändernde Vertex zeigt also auf die aktuelle Zeigerposition im VBO und schreiben dann unser Vertex direkt in den Grafikkartenspeicher. Ist dies getan, müssen wir die Adresse unseres VBOPointers natürlich um die Größe des gerade geschriebenen Vertexes erhöhen. Der Typecast auf ein Integer geschieht deshalb, weil man Pointer in Delphi nicht so einfach erhöhen oder verringern kann, Integers hingegen schon.&lt;br /&gt;
&lt;br /&gt;
Ich gehe mal davon aus,das meine Leserschaft mit obigem Codeschnippsel keinerlei Probleme hatte und bringe diese Methode der Übergabe von Vertexdaten an das VBO dann auch mit folgender Quellcodezeile zu Ende:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glUnMapBuffer(GL_ARRAY_BUFFER);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie unschwer zu erraten geben wir hier unser VBO für den weiteren Zugriff wieder frei. Dies ist nötig um dies später rendern zu können.&lt;br /&gt;
&lt;br /&gt;
==Übergabe der Vertexdaten (Variante 2: Über den Hauptspeicher)==&lt;br /&gt;
&lt;br /&gt;
Wem obige Variante nicht zugesagt hat, weil sie ihm zu komplex war, oder weil er seine Vertexdaten aus diversen Gründen im Hauptspeicher braucht, dem wird in diesem Abschnitt geholfen.&lt;br /&gt;
&lt;br /&gt;
Das Übergeben der Vertexdaten an das VBO über den Hauptspeicher gestaltet sich nämlich sehr einfach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBufferData(GL_ARRAY_BUFFER, SizeOf(VertexDaten), @VertexDaten, GL_STATIC_DRAW);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sieht einfach aus,hört sich einfach an und ist auch einfach! Statt wie im letzten Kapitel für die Vertexdaten einen Zeiger auf '''nil''' zu übergeben, übergeben wir OpenGL jetzt einen direkten Zeiger auf die Vertexdaten die im Hauptspeicher abgelegt sind. Die Daten werden dann zum VBO hochgeladen, und können anschliessen auch wieder aus dem Hauptspeicher entfernt werden.&lt;br /&gt;
&lt;br /&gt;
==Rendern des VBOs==&lt;br /&gt;
&lt;br /&gt;
Kommen wir nun also schon zum Ende unseres VBO-Tutorials. Dank der Einfachheit dieser Extension gabs halt einfach nicht mehr zu erklären. Das Zeichnen gestaltet sich nämlich OpenGL-typisch mehr als einfach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glInterleavedArrays(GL_T2F_V3F, SizeOf(TVertex), nil);&lt;br /&gt;
glDrawArrays(GL_QUADS, 0, VertexDatenLaenge);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schnell gezeichnet und schnell erklärt. Der erste Parameter gibt an, in welchem Format die Vertexdaten unseres VBOs abgelegt wurden (wir erinnern uns: '''GL_T2F_V3F'''), während der zweite Parameter den Speicherplatz, der zwischen zwei Vertices liegt, angibt. Würden wir ein einfaches Vertexarray zeichnen, so wäre der letzte Parameter ein Pointer auf die Vertexdaten im Hauptspeicher. Da wir allerdings vorher unser VBO gebunden haben, teilt hier ein '''nil''' mit, dass die vorher ins VBO geschriebenen Vertexdaten gerendert werden sollen.&lt;br /&gt;
&lt;br /&gt;
Mittels [[glDrawArrays]] sagen wir OpenGL dann noch, das es unsere VBO-Daten als Quads rendern soll.&lt;br /&gt;
&lt;br /&gt;
==Freigabe==&lt;br /&gt;
&lt;br /&gt;
Wie mit allen Objekten in der OpenGL ists auch keine schlechte Idee, unser VBO freizugeben,wenn es nicht mehr benötigt wird. Und obwohl beim Löschen des Renderkontextes solche Objekte vom Grafikkartentreiber freigegeben werden sollten, wäre es keine schlechte Idee des selbst zu erledigen,für den Fall das der Treiber da schlampig arbeitet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glDeleteBuffers(1, @VBO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Weitere Vertexformate==&lt;br /&gt;
Wem allein Texturkoordinaten und Vertices nicht reichen, sondern auch Farbangaben oder Vertexnormalen braucht, muss sich eines der folgenden Formate aussuchen. Die Konstantennamen sind logisch aufgebaut: ''GL_AngabeAnzahlTyp_AngabeAnzahlTyp...''&lt;br /&gt;
Als Angabe gibt es '''V'''ertex, '''T'''exturkoordinaten, '''N'''ormalen und '''C'''olor-Werte, also Farben.&lt;br /&gt;
Anzahl ist die Anzahl der Komponenten pro Angabe (V3 wären zum Bespiel die X, Y und Z-Koordinaten eines dreidimensionalen Vertex).&lt;br /&gt;
Typ ist entweder '''F'''loat oder '''U'''nsigned '''B'''yte. Die Vertexdaten müssen in der richtigen Reihenfolge mit den richtigen Typen an das VBO übergeben werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  GL_V2F&lt;br /&gt;
  GL_V3F&lt;br /&gt;
  GL_C4UB_V2F&lt;br /&gt;
  GL_C4UB_V3F&lt;br /&gt;
  GL_C3F_V3F           &lt;br /&gt;
  GL_N3F_V3F           &lt;br /&gt;
  GL_C4F_N3F_V3F       &lt;br /&gt;
  GL_T2F_V3F&lt;br /&gt;
  GL_T4F_V4F&lt;br /&gt;
  GL_T2F_C4UB_V3F&lt;br /&gt;
  GL_T2F_C3F_V3F&lt;br /&gt;
  GL_T2F_N3F_V3F      // Wohl eines der sinnvollsten Vertexformate. 2 Floats für Texturkoordinaten, 3 Floats für den Normalenvektor und 3 Floats für den eigentlichen Vertex.&lt;br /&gt;
  GL_T2F_C4F_N3F_V3F&lt;br /&gt;
  GL_T4F_C4F_N3F_V4F&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es ist sinnvoll, sich für jedes der Vertexformate, die man verwendet, einen Typen anzulegen, um später einfacher mit den VBOs arbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Falls man mit Shadern arbeitet kann man sich die Wahl des Vertexformates eigentlich sparen, denn hier kann man z.B. für jeden Attributtyp den man braucht (Vertex, Normale, Farbe, Normale, etc.) ein eigenes VBO anlegen und dieses dann entsprechend seinen Bedürfnissen im Shader interpretieren, oder einfach nur ein riesiges VBO nutzen und dort alle Daten nach Belieben ablegen und im Shader selbst interpretieren.&lt;br /&gt;
&lt;br /&gt;
==Nachwort==&lt;br /&gt;
&lt;br /&gt;
Das wars auch schon. Wie zu erkennen ist das Vertexbufferobjekt mal wieder eine gut durchdachte, recht nützliche und zudem auch weit nutzbare Erweiterung, die OpenGL technisch wieder einen Schritt weitergebracht hat.&lt;br /&gt;
&lt;br /&gt;
Wer mehr wissen will, der sollte sich unbedingt mal die passende [http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt Spezifikation] ansehen.&lt;br /&gt;
&lt;br /&gt;
Hoffe das Tutorial hat euch soviel Spaß gemacht wie mir das Verfassen des selbigen und ich hoffe auf reges Feedback!&lt;br /&gt;
&lt;br /&gt;
Euer&lt;br /&gt;
&lt;br /&gt;
Sascha Willems (webmaster_at_delphigl.de)&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[http://developer.nvidia.com/attach/6427 Nvidia: Using Vertex Buffer Objects]&lt;br /&gt;
&lt;br /&gt;
[http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt Spezifikation]&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_Cubemap]]|[[Tutorial_Vertexprogramme]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Vertexbuffer]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Vertexbufferobject&amp;diff=25086</id>
		<title>Tutorial Vertexbufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Vertexbufferobject&amp;diff=25086"/>
				<updated>2011-05-04T08:22:41Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Erstellen des Vertexbufferobjects */ ARB entfernt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=GL_ARB_Vertex_Buffer_Object=&lt;br /&gt;
==Ave!==&lt;br /&gt;
&lt;br /&gt;
Und willkommen zu meinem zweiten GL_ARB-Extension Tutorial. Diesmal beschäftigen wir uns mit der recht neuen '''GL_ARB_Vertex_Buffer_Object'''-Extension, die vom ARB am 12.Februar 2003 fertiggestellt und später im Jahr 2003 auch als Corefeature in OpenGL 1.5 promoted wurde.&lt;br /&gt;
&lt;br /&gt;
VBOs (Vertex Buffer Objects) sind quasi eine Erweiterung der recht weit verbreiteten Vertexarrays, die jedoch den großen Vorteil besitzen, dass ihre Vertexdaten serverseitig, also im schnellen VRAM der Grafikkarte, statt wie bei den VAs im Hauptspeicher, abgelegt werden. Das ist bei einer Displayliste zwar (fast) genauso der Fall, allerdings können die Daten nicht so einfach dynamisch geändert werden, was bei einem VBO aber bei Bedarf sehr einfach ist.&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt vereint das VBO also die Flexibilität eines Vertexarrays mit der Geschwindigkeit einer Displayliste und eignet sich daher besonders für das Rendern aufwändiger Geometrie, egal ob statisch oder dynamisch.&lt;br /&gt;
&lt;br /&gt;
Eine weitere tolle Neuerung der VBO-API ist die Tatsache, dass man sich von OpenGL einen Pointer übergeben lassen kann, mit dessen Hilfe man Vertexdaten direkt in den VRAM schreiben kann. Dadurch spart man sich natürlich den Umweg über den Hauptspeicher und somit auch den erhöhten Speicherbedarf des Programmes beim Laden von Modellen o.&amp;amp;nbsp;Ä.&lt;br /&gt;
&lt;br /&gt;
VBOs bieten übrigens auch die Möglichkeit, sie je nach Anwendungsfall vom Grafikkartentreiber &amp;quot;optimieren&amp;quot; zu lassen. So legt man Vertexdaten, die im Nachhinein nicht mehr verändert werden sollenm am besten mit dem Schlüsselwort ''STATIC_DRAW'' ab, während dynamische Vertexdaten über ''DYNAMIC_DRAW'' abgelegt werden sollten. Doch dazu gibts später nähere Informationen.&lt;br /&gt;
&lt;br /&gt;
==Hardwareunterstützung==&lt;br /&gt;
&lt;br /&gt;
Inzwischen sollten alle gängigen Grafikkarten diese Extension problemlos unterstützen, ältere Modelle tun dies aber evtl. nur emuliert über den Treiber. Will man sein Programm auch für sehr alte Hardware (Karten älter als ATIs Radeon 9x00 bzw. NVidias GeForce 4-Serie) lauffähig machen, sollte man noch einen alternativen Renderpfad auf Basis von Displaylisten einbauen.&lt;br /&gt;
&lt;br /&gt;
==Erstellen des Vertexbufferobjects==&lt;br /&gt;
&lt;br /&gt;
Wie der Name vermuten lässt, handelt es sich beim VBO um ein Objekt, welches in Sachen Syntax an die anderen in OpenGL verfügbaren Objekte wie z.&amp;amp;nbsp;B. Texturenobjekte angelehnt ist. Das Erstellen eines VBOs geht daher genauso einfach von der Hand:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glGenBuffers(1, @VBO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun sollten wir in VBO eine gültige ID zurückgeliefert bekommen haben, welche ein Vertex buffer object referenziert.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun ein VBO erstellt haben, sollten wir dieses natürlich auch &amp;quot;aktivieren&amp;quot; (also binden), damit wir mit ihm arbeiten können:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBindBuffer(GL_ARRAY_BUFFER, VBO);&lt;br /&gt;
glEnableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie erwähnt binden wir in der ersten Zeile unser frisch erstelltes VBO, während wir in der zweiten Zeile Vertexarrays auf der Clientseite aktivieren. Dies ist deshalb nötig, da wir das VBO später genauso zeichnen werden wie dies bei einem normalen VA der Fall wäre.&lt;br /&gt;
&lt;br /&gt;
==Übergabe der Vertexdaten (Variante 1: Ohne Umweg über den Hauptspeicher)==&lt;br /&gt;
&lt;br /&gt;
Wie in der Einleitung bereits erwähnt, bietet uns die VBO-API die Möglichkeit, unsere Vertexdaten ohne Umweg über den Hauptspeicher direkt in den Grafikkartenspeicher zu schreiben. Da dies eines der besten Features dieser neuen Extension ist, wollen wir uns auch zuerst mit dieser Übergabemethode beschäftigen.&lt;br /&gt;
&lt;br /&gt;
''Allerdings erstmal eine kleine Warnung vorweg: Pointer sind ein recht komplexes Thema, und ihre Nutzung erfordert einige Kenntnisse. Wer weder weiß, wie man damit umgeht, noch wie man sie einsetzt, sollte sich da zuerstmal schlau machen. Denn mit Pointer kann man ganz böse Sachen machen, wenn man sie nicht richtig nutzen kann!''&lt;br /&gt;
&lt;br /&gt;
Um unsere Vertexdaten als Pointer zu nutzen,müssen wir erstmal eine Vertexstruktur erstellen und auch einen passenden Pointertyp. Der Pointertyp ist zwar nicht zwingend (ein ^ täte es auch), aber macht die Sache übersichtlicher:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
PVertex = ^TVertex;&lt;br /&gt;
TVertex = packed record&lt;br /&gt;
 S,T,X,Y,Z : TGLFloat;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sicher fragen sich einige von euch jetzt, warum unser TVertex gerade so aussieht (also zuerst S,T und dann der Rest). Das liegt daran, dass wir unser VBO später über die Funktion [[glInterleavedArrays]] rendern werden, welche wissen will, wie unsere Vertexdaten im Speicher abgelegt sind. Unsere Vertexstruktur entspricht dabei dem Format ''GL_T2F_V3F''. ''T2F'' entspricht 2 Floats für die Texturenkoordinaten (S&amp;amp;T), gefolgt von ''V3F'' für die drei Floatwerte die unsere Vertexkoordinaten repräsentieren. Wenn ihr ein anderes Vertexformat verwendet, müsst ihr euch auch ein passendes Vertexformat aus der Spezifikation heraussuchen.&lt;br /&gt;
Ich verwende oben übrigens einen '''packed Record''' (auch wenn dies bei unserem Vertexformat nicht unbedingt ein Muss ist). Dies tue ich deshalb, da Delphi Werte bei einem normalen Record für einen beschleunigten Zugriff an einem (Double)Word-Raster ausrichtet und dies deshalb bei bestimmten Vertexformaten Probleme bereiten könnte.&lt;br /&gt;
&lt;br /&gt;
Hoffe mal das euch obiges klar ist,denn jetzt gehts ans Eingemachte. Wir kommen zum &amp;quot;schwersten&amp;quot; (ist ja ein relativer Begriff) Teil des Tutorials, nämlich der Übergabe der Vertexdaten an das VBO. Dazu müssen wir OpenGL zuerstmal mitteilen, wie groß unser VBO eigentlich sein soll,damit wir auch genug Platz im VRAM bekommen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBufferData(GL_ARRAY_BUFFER, VertexAnzahl*SizeOf(TVertex), nil, GL_STATIC_DRAW);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schauen wir uns also mal die Parameter von [[glBufferData]] an. Zuerst müssen wir OpenGL mitteilen, was für einen Puffer wir erstellen wollen (in zukünftigen Versionen werden wohl noch andere folgen, z.&amp;amp;nbsp;B. Überbuffer). In unserem Falle also einen Arraypuffer. Parameter zwei übergibt dann den Speicherplatz, den wir reservieren wollen und ist recht selbsterklärend. Der dritte Parameter würde normalerweise einen Pointer auf unsere Vertexdaten enthalten. Da wir diese aber nicht im Hauptspeicher abgelegt haben, sondern über einen Pointer dort noch ablegen wollen, übergeben wir hier '''nil'''. OpenGL nimmt dies zur Kenntnis und spuckt dementsprechend auch keinen Fehler aus.&lt;br /&gt;
&lt;br /&gt;
Den letzten Parameter hab ich auch schon kurz in der Einführung angesprochen. Er teilt OpenGL mit, auf welchen Anwendungsfall unser VBO optimiert werden soll. Dabei gibt es folgende Anwendungsfälle,die ihr euch dann entsprechend eurer VBO-Verwendung aussuchen könnt:&lt;br /&gt;
&lt;br /&gt;
{|rules=&amp;quot;all&amp;quot; border = &amp;quot;1&amp;quot;&lt;br /&gt;
! STREAM_DRAW&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann eher selten als Quelle für OpenGL-Befehle genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STREAM_READ&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann selten von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! STREAM_COPY&lt;br /&gt;
| Die Vertexdaten werden einmal durch Kopieren von der GL übergeben und dann eher selten als Quelle für OpenGL-Befehle genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_DRAW&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_READ&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann recht oft von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_COPY&lt;br /&gt;
| Die Vertexdaten werden einmal durch Kopieren von der GL übergeben und dann recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_DRAW&lt;br /&gt;
| Die Vertexdaten werden wiederholt angegeben (verändert) und recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_READ&lt;br /&gt;
| Die Vertexdaten werden wiederholt durch das Auslesen von Daten durch OpenGL angegeben (verändert) und oft von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_COPY&lt;br /&gt;
| Die Vertexdaten werden wiederholt durch das Auslesen von Daten durch OpenGL angegeben (verändert) und recht oft zum Rendern genutzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beachtet werden sollte, dass es sich bei obigen Anwendungsmuster nur um Hinweise (Hints) handelt, die es auch in anderen Bereichen von OpenGL gibt. Diese Hinweise sind allerdings nicht verbindlich und werden von Treiber zu Treiber unterschiedlich behandelt.&lt;br /&gt;
&lt;br /&gt;
Nachdem OpenGL nun weiss was für ein VBO wir genau haben wollen, brauchen wir letztendlich nur noch einen Pointer, der &amp;quot;auf&amp;quot; das VBO zeigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
VBOPointer := glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion [[glMapBuffer]] liefert uns einen Pointer zurück, der den Speicherplatz des VBOs in den Adressraum des Clients &amp;quot;ummappt&amp;quot;, also in den Hauptspeicher. Dadurch wird es dann möglich, über eine Adresse im Hauptspeicher des Rechners direkt in den Grafikkartenspeicher zu schreiben. Der zweite Parameter gibt übrigens an, das wir in das VBO schreiben wollen. Der einzige weitere zulässige Parameter ist hier '''GL_READ_ONLY''', welcher zum Auslesen des VBOs dient.&lt;br /&gt;
&lt;br /&gt;
Endlich haben wir einen Pointer auf unser inzwischen auch im VRAM der Grafikkarte richtig dimensioniertes Vertexbufferobject, und sind nun in der Lage dort unsere Vertexdaten abzulegen. Was jetzt folgt ist ein wenig Pseudo-Code der diesen Vorgang darstellt. Pseudo-Code deshalb, weil die Vertexdaten die ins VBO kommen ja in jeder Anwendung unterschiedlich sind, und man so auch selbst ein wenig denken muss:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
VertexDatenLaenge := 0;&lt;br /&gt;
for i := 0 to High(MeineVertexDaten) do&lt;br /&gt;
 begin&lt;br /&gt;
 VertexPointer    := VBOPointer;&lt;br /&gt;
&lt;br /&gt;
 VertexPointer^.X := GeneriertesVertex.X;&lt;br /&gt;
 VertexPointer^.Y := GeneriertesVertex.Y;&lt;br /&gt;
 VertexPointer^.Z := GeneriertesVertex.Z;&lt;br /&gt;
 VertexPointer^.S := GeneriertesVertex.S;&lt;br /&gt;
 VertexPointer^.T := GeneriertesVertex.T;&lt;br /&gt;
&lt;br /&gt;
 inc(Integer(VBOPointer), SizeOf(TVertex));&lt;br /&gt;
&lt;br /&gt;
 inc(VertexDatenLaenge);&lt;br /&gt;
 end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da sich dieses Tutorial (und v.&amp;amp;nbsp;a. diese Methode der Vertexdatenübergabe) an die fortgeschritteneren wendet, rede ich nicht lange um den heissen Brei herum. Wir durchlaufen also eine Schleife, in der wir unsere Vertexdaten dynamisch erstellen. Entweder in jedem Durchlauf, oder wir laden die Daten vor der Schleife z.&amp;amp;nbsp;B. aus einem 3D-Modell und entfernen sie danach aus dem Hauptspeicher. Erste Methode spart sowohl beim Programmstart, also auch während des Programmlaufes Arbeitsspeicher, während letztere dies nur während des Programmablaufs macht.&lt;br /&gt;
&lt;br /&gt;
Wir setzen die Adresse des Pointers der auf das momentan zu verändernde Vertex zeigt also auf die aktuelle Zeigerposition im VBO und schreiben dann unser Vertex direkt in den Grafikkartenspeicher. Ist dies getan, müssen wir die Adresse unseres VBOPointers natürlich um die Größe des gerade geschriebenen Vertexes erhöhen. Der Typecast auf ein Integer geschieht deshalb, weil man Pointer in Delphi nicht so einfach erhöhen oder verringern kann, Integers hingegen schon.&lt;br /&gt;
&lt;br /&gt;
Ich gehe mal davon aus,das meine Leserschaft mit obigem Codeschnippsel keinerlei Probleme hatte und bringe diese Methode der Übergabe von Vertexdaten an das VBO dann auch mit folgender Quellcodezeile zu Ende:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glUnMapBuffer(GL_ARRAY_BUFFER);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie unschwer zu erraten geben wir hier unser VBO für den weiteren Zugriff wieder frei. Dies ist nötig um dies später rendern zu können.&lt;br /&gt;
&lt;br /&gt;
==Übergabe der Vertexdaten (Variante 2: Über den Hauptspeicher)==&lt;br /&gt;
&lt;br /&gt;
Wem obige Variante nicht zugesagt hat, weil sie ihm zu komplex war, oder weil er seine Vertexdaten aus diversen Gründen im Hauptspeicher braucht, dem wird in diesem Abschnitt geholfen.&lt;br /&gt;
&lt;br /&gt;
Das Übergeben der Vertexdaten an das VBO über den Hauptspeicher gestaltet sich nämlich sehr einfach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBufferData(GL_ARRAY_BUFFER, SizeOf(VertexDaten), @VertexDaten, GL_STATIC_DRAW);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sieht einfach aus,hört sich einfach an und ist auch einfach! Statt wie im letzten Kapitel für die Vertexdaten einen Zeiger auf '''nil''' zu übergeben, übergeben wir OpenGL jetzt einen direkten Zeiger auf die Vertexdaten die im Hauptspeicher abgelegt sind. Die Daten werden dann zum VBO hochgeladen, und können anschliessen auch wieder aus dem Hauptspeicher entfernt werden.&lt;br /&gt;
&lt;br /&gt;
==Rendern des VBOs==&lt;br /&gt;
&lt;br /&gt;
Kommen wir nun also schon zum Ende unseres VBO-Tutorials. Dank der Einfachheit dieser Extension gabs halt einfach nicht mehr zu erklären. Das Zeichnen gestaltet sich nämlich OpenGL-typisch mehr als einfach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glInterleavedArrays(GL_T2F_V3F, SizeOf(TVertex), nil);&lt;br /&gt;
glDrawArrays(GL_QUADS, 0, VertexDatenLaenge);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schnell gezeichnet und schnell erklärt. Der erste Parameter gibt an, in welchem Format die Vertexdaten unseres VBOs abgelegt wurden (wir erinnern uns: '''GL_T2F_V3F'''), während der zweite Parameter den Speicherplatz, der zwischen zwei Vertices liegt, angibt. Würden wir ein einfaches Vertexarray zeichnen, so wäre der letzte Parameter ein Pointer auf die Vertexdaten im Hauptspeicher. Da wir allerdings vorher unser VBO gebunden haben, teilt hier ein '''nil''' mit, dass die vorher ins VBO geschriebenen Vertexdaten gerendert werden sollen.&lt;br /&gt;
&lt;br /&gt;
Mittels [[glDrawArrays]] sagen wir OpenGL dann noch, das es unsere VBO-Daten als Quads rendern soll.&lt;br /&gt;
&lt;br /&gt;
==Freigabe==&lt;br /&gt;
&lt;br /&gt;
Wie mit allen Objekten in der OpenGL ists auch keine schlechte Idee, unser VBO freizugeben,wenn es nicht mehr benötigt wird. Und obwohl beim Löschen des Renderkontextes solche Objekte vom Grafikkartentreiber freigegeben werden sollten, wäre es keine schlechte Idee des selbst zu erledigen,für den Fall das der Treiber da schlampig arbeitet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glDeleteBuffers(1, @VBO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Weitere Vertexformate==&lt;br /&gt;
Wem allein Texturkoordinaten und Vertices nicht reichen, sondern auch Farbangaben oder Vertexnormalen braucht, muss sich eines der folgenden Formate aussuchen. Die Konstantennamen sind logisch aufgebaut: ''GL_AngabeAnzahlTyp_AngabeAnzahlTyp...''&lt;br /&gt;
Als Angabe gibt es '''V'''ertex, '''T'''exturkoordinaten, '''N'''ormalen und '''C'''olor-Werte, also Farben.&lt;br /&gt;
Anzahl ist die Anzahl der Komponenten pro Angabe (V3 wären zum Bespiel die X, Y und Z-Koordinaten eines dreidimensionalen Vertex).&lt;br /&gt;
Typ ist entweder '''F'''loat oder '''U'''nsigned '''B'''yte. Die Vertexdaten müssen in der richtigen Reihenfolge mit den richtigen Typen an das VBO übergeben werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  GL_V2F&lt;br /&gt;
  GL_V3F&lt;br /&gt;
  GL_C4UB_V2F&lt;br /&gt;
  GL_C4UB_V3F&lt;br /&gt;
  GL_C3F_V3F           &lt;br /&gt;
  GL_N3F_V3F           &lt;br /&gt;
  GL_C4F_N3F_V3F       &lt;br /&gt;
  GL_T2F_V3F&lt;br /&gt;
  GL_T4F_V4F&lt;br /&gt;
  GL_T2F_C4UB_V3F&lt;br /&gt;
  GL_T2F_C3F_V3F&lt;br /&gt;
  GL_T2F_N3F_V3F      // Wohl eines der sinnvollsten Vertexformate. 2 Floats für Texturkoordinaten, 3 Floats für den Normalenvektor und 3 Floats für den eigentlichen Vertex.&lt;br /&gt;
  GL_T2F_C4F_N3F_V3F&lt;br /&gt;
  GL_T4F_C4F_N3F_V4F&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es ist sinnvoll, sich für jedes der Vertexformate, die man verwendet, einen Typen anzulegen, um später einfacher mit den VBOs arbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Falls man mit Shadern arbeitet kann man sich die Wahl des Vertexformates eigentlich sparen, denn hier kann man z.B. für jeden Attributtyp den man braucht (Vertex, Normale, Farbe, Normale, etc.) ein eigenes VBO anlegen und dieses dann entsprechend seinen Bedürfnissen im Shader interpretieren, oder einfach nur ein riesiges VBO nutzen und dort alle Daten nach Belieben ablegen und im Shader selbst interpretieren.&lt;br /&gt;
&lt;br /&gt;
==Nachwort==&lt;br /&gt;
&lt;br /&gt;
Das wars auch schon. Wie zu erkennen ist das Vertexbufferobjekt mal wieder eine gut durchdachte, recht nützliche und zudem auch weit nutzbare Erweiterung, die OpenGL technisch wieder einen Schritt weitergebracht hat.&lt;br /&gt;
&lt;br /&gt;
Wer mehr wissen will, der sollte sich unbedingt mal die passende [http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt Spezifikation] ansehen.&lt;br /&gt;
&lt;br /&gt;
Hoffe das Tutorial hat euch soviel Spaß gemacht wie mir das Verfassen des selbigen und ich hoffe auf reges Feedback!&lt;br /&gt;
&lt;br /&gt;
Euer&lt;br /&gt;
&lt;br /&gt;
Sascha Willems (webmaster_at_delphigl.de)&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[http://developer.nvidia.com/attach/6427 Nvidia: Using Vertex Buffer Objects]&lt;br /&gt;
&lt;br /&gt;
[http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt Spezifikation]&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_Cubemap]]|[[Tutorial_Vertexprogramme]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Vertexbuffer]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Vertexbufferobject&amp;diff=25085</id>
		<title>Tutorial Vertexbufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Vertexbufferobject&amp;diff=25085"/>
				<updated>2011-05-04T08:22:20Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Übergabe der Vertexdaten (Variante 1: Ohne Umweg über den Hauptspeicher) */ ARB entfernt...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=GL_ARB_Vertex_Buffer_Object=&lt;br /&gt;
==Ave!==&lt;br /&gt;
&lt;br /&gt;
Und willkommen zu meinem zweiten GL_ARB-Extension Tutorial. Diesmal beschäftigen wir uns mit der recht neuen '''GL_ARB_Vertex_Buffer_Object'''-Extension, die vom ARB am 12.Februar 2003 fertiggestellt und später im Jahr 2003 auch als Corefeature in OpenGL 1.5 promoted wurde.&lt;br /&gt;
&lt;br /&gt;
VBOs (Vertex Buffer Objects) sind quasi eine Erweiterung der recht weit verbreiteten Vertexarrays, die jedoch den großen Vorteil besitzen, dass ihre Vertexdaten serverseitig, also im schnellen VRAM der Grafikkarte, statt wie bei den VAs im Hauptspeicher, abgelegt werden. Das ist bei einer Displayliste zwar (fast) genauso der Fall, allerdings können die Daten nicht so einfach dynamisch geändert werden, was bei einem VBO aber bei Bedarf sehr einfach ist.&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt vereint das VBO also die Flexibilität eines Vertexarrays mit der Geschwindigkeit einer Displayliste und eignet sich daher besonders für das Rendern aufwändiger Geometrie, egal ob statisch oder dynamisch.&lt;br /&gt;
&lt;br /&gt;
Eine weitere tolle Neuerung der VBO-API ist die Tatsache, dass man sich von OpenGL einen Pointer übergeben lassen kann, mit dessen Hilfe man Vertexdaten direkt in den VRAM schreiben kann. Dadurch spart man sich natürlich den Umweg über den Hauptspeicher und somit auch den erhöhten Speicherbedarf des Programmes beim Laden von Modellen o.&amp;amp;nbsp;Ä.&lt;br /&gt;
&lt;br /&gt;
VBOs bieten übrigens auch die Möglichkeit, sie je nach Anwendungsfall vom Grafikkartentreiber &amp;quot;optimieren&amp;quot; zu lassen. So legt man Vertexdaten, die im Nachhinein nicht mehr verändert werden sollenm am besten mit dem Schlüsselwort ''STATIC_DRAW'' ab, während dynamische Vertexdaten über ''DYNAMIC_DRAW'' abgelegt werden sollten. Doch dazu gibts später nähere Informationen.&lt;br /&gt;
&lt;br /&gt;
==Hardwareunterstützung==&lt;br /&gt;
&lt;br /&gt;
Inzwischen sollten alle gängigen Grafikkarten diese Extension problemlos unterstützen, ältere Modelle tun dies aber evtl. nur emuliert über den Treiber. Will man sein Programm auch für sehr alte Hardware (Karten älter als ATIs Radeon 9x00 bzw. NVidias GeForce 4-Serie) lauffähig machen, sollte man noch einen alternativen Renderpfad auf Basis von Displaylisten einbauen.&lt;br /&gt;
&lt;br /&gt;
==Erstellen des Vertexbufferobjects==&lt;br /&gt;
&lt;br /&gt;
Wie der Name vermuten lässt, handelt es sich beim VBO um ein Objekt, welches in Sachen Syntax an die anderen in OpenGL verfügbaren Objekte wie z.&amp;amp;nbsp;B. Texturenobjekte angelehnt ist. Das Erstellen eines VBOs geht daher genauso einfach von der Hand:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glGenBuffers(1, @VBO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun sollten wir in VBO eine gültige ID zurückgeliefert bekommen haben, welche ein Vertex buffer object referenziert.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun ein VBO erstellt haben, sollten wir dieses natürlich auch &amp;quot;aktivieren&amp;quot; (also binden), damit wir mit ihm arbeiten können:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBindBufferARB(GL_ARRAY_BUFFER, VBO);&lt;br /&gt;
glEnableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie erwähnt binden wir in der ersten Zeile unser frisch erstelltes VBO, während wir in der zweiten Zeile Vertexarrays auf der Clientseite aktivieren. Dies ist deshalb nötig, da wir das VBO später genauso zeichnen werden wie dies bei einem normalen VA der Fall wäre.&lt;br /&gt;
&lt;br /&gt;
==Übergabe der Vertexdaten (Variante 1: Ohne Umweg über den Hauptspeicher)==&lt;br /&gt;
&lt;br /&gt;
Wie in der Einleitung bereits erwähnt, bietet uns die VBO-API die Möglichkeit, unsere Vertexdaten ohne Umweg über den Hauptspeicher direkt in den Grafikkartenspeicher zu schreiben. Da dies eines der besten Features dieser neuen Extension ist, wollen wir uns auch zuerst mit dieser Übergabemethode beschäftigen.&lt;br /&gt;
&lt;br /&gt;
''Allerdings erstmal eine kleine Warnung vorweg: Pointer sind ein recht komplexes Thema, und ihre Nutzung erfordert einige Kenntnisse. Wer weder weiß, wie man damit umgeht, noch wie man sie einsetzt, sollte sich da zuerstmal schlau machen. Denn mit Pointer kann man ganz böse Sachen machen, wenn man sie nicht richtig nutzen kann!''&lt;br /&gt;
&lt;br /&gt;
Um unsere Vertexdaten als Pointer zu nutzen,müssen wir erstmal eine Vertexstruktur erstellen und auch einen passenden Pointertyp. Der Pointertyp ist zwar nicht zwingend (ein ^ täte es auch), aber macht die Sache übersichtlicher:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
PVertex = ^TVertex;&lt;br /&gt;
TVertex = packed record&lt;br /&gt;
 S,T,X,Y,Z : TGLFloat;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sicher fragen sich einige von euch jetzt, warum unser TVertex gerade so aussieht (also zuerst S,T und dann der Rest). Das liegt daran, dass wir unser VBO später über die Funktion [[glInterleavedArrays]] rendern werden, welche wissen will, wie unsere Vertexdaten im Speicher abgelegt sind. Unsere Vertexstruktur entspricht dabei dem Format ''GL_T2F_V3F''. ''T2F'' entspricht 2 Floats für die Texturenkoordinaten (S&amp;amp;T), gefolgt von ''V3F'' für die drei Floatwerte die unsere Vertexkoordinaten repräsentieren. Wenn ihr ein anderes Vertexformat verwendet, müsst ihr euch auch ein passendes Vertexformat aus der Spezifikation heraussuchen.&lt;br /&gt;
Ich verwende oben übrigens einen '''packed Record''' (auch wenn dies bei unserem Vertexformat nicht unbedingt ein Muss ist). Dies tue ich deshalb, da Delphi Werte bei einem normalen Record für einen beschleunigten Zugriff an einem (Double)Word-Raster ausrichtet und dies deshalb bei bestimmten Vertexformaten Probleme bereiten könnte.&lt;br /&gt;
&lt;br /&gt;
Hoffe mal das euch obiges klar ist,denn jetzt gehts ans Eingemachte. Wir kommen zum &amp;quot;schwersten&amp;quot; (ist ja ein relativer Begriff) Teil des Tutorials, nämlich der Übergabe der Vertexdaten an das VBO. Dazu müssen wir OpenGL zuerstmal mitteilen, wie groß unser VBO eigentlich sein soll,damit wir auch genug Platz im VRAM bekommen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBufferData(GL_ARRAY_BUFFER, VertexAnzahl*SizeOf(TVertex), nil, GL_STATIC_DRAW);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schauen wir uns also mal die Parameter von [[glBufferData]] an. Zuerst müssen wir OpenGL mitteilen, was für einen Puffer wir erstellen wollen (in zukünftigen Versionen werden wohl noch andere folgen, z.&amp;amp;nbsp;B. Überbuffer). In unserem Falle also einen Arraypuffer. Parameter zwei übergibt dann den Speicherplatz, den wir reservieren wollen und ist recht selbsterklärend. Der dritte Parameter würde normalerweise einen Pointer auf unsere Vertexdaten enthalten. Da wir diese aber nicht im Hauptspeicher abgelegt haben, sondern über einen Pointer dort noch ablegen wollen, übergeben wir hier '''nil'''. OpenGL nimmt dies zur Kenntnis und spuckt dementsprechend auch keinen Fehler aus.&lt;br /&gt;
&lt;br /&gt;
Den letzten Parameter hab ich auch schon kurz in der Einführung angesprochen. Er teilt OpenGL mit, auf welchen Anwendungsfall unser VBO optimiert werden soll. Dabei gibt es folgende Anwendungsfälle,die ihr euch dann entsprechend eurer VBO-Verwendung aussuchen könnt:&lt;br /&gt;
&lt;br /&gt;
{|rules=&amp;quot;all&amp;quot; border = &amp;quot;1&amp;quot;&lt;br /&gt;
! STREAM_DRAW&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann eher selten als Quelle für OpenGL-Befehle genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STREAM_READ&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann selten von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! STREAM_COPY&lt;br /&gt;
| Die Vertexdaten werden einmal durch Kopieren von der GL übergeben und dann eher selten als Quelle für OpenGL-Befehle genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_DRAW&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_READ&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann recht oft von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_COPY&lt;br /&gt;
| Die Vertexdaten werden einmal durch Kopieren von der GL übergeben und dann recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_DRAW&lt;br /&gt;
| Die Vertexdaten werden wiederholt angegeben (verändert) und recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_READ&lt;br /&gt;
| Die Vertexdaten werden wiederholt durch das Auslesen von Daten durch OpenGL angegeben (verändert) und oft von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_COPY&lt;br /&gt;
| Die Vertexdaten werden wiederholt durch das Auslesen von Daten durch OpenGL angegeben (verändert) und recht oft zum Rendern genutzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beachtet werden sollte, dass es sich bei obigen Anwendungsmuster nur um Hinweise (Hints) handelt, die es auch in anderen Bereichen von OpenGL gibt. Diese Hinweise sind allerdings nicht verbindlich und werden von Treiber zu Treiber unterschiedlich behandelt.&lt;br /&gt;
&lt;br /&gt;
Nachdem OpenGL nun weiss was für ein VBO wir genau haben wollen, brauchen wir letztendlich nur noch einen Pointer, der &amp;quot;auf&amp;quot; das VBO zeigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
VBOPointer := glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion [[glMapBuffer]] liefert uns einen Pointer zurück, der den Speicherplatz des VBOs in den Adressraum des Clients &amp;quot;ummappt&amp;quot;, also in den Hauptspeicher. Dadurch wird es dann möglich, über eine Adresse im Hauptspeicher des Rechners direkt in den Grafikkartenspeicher zu schreiben. Der zweite Parameter gibt übrigens an, das wir in das VBO schreiben wollen. Der einzige weitere zulässige Parameter ist hier '''GL_READ_ONLY''', welcher zum Auslesen des VBOs dient.&lt;br /&gt;
&lt;br /&gt;
Endlich haben wir einen Pointer auf unser inzwischen auch im VRAM der Grafikkarte richtig dimensioniertes Vertexbufferobject, und sind nun in der Lage dort unsere Vertexdaten abzulegen. Was jetzt folgt ist ein wenig Pseudo-Code der diesen Vorgang darstellt. Pseudo-Code deshalb, weil die Vertexdaten die ins VBO kommen ja in jeder Anwendung unterschiedlich sind, und man so auch selbst ein wenig denken muss:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
VertexDatenLaenge := 0;&lt;br /&gt;
for i := 0 to High(MeineVertexDaten) do&lt;br /&gt;
 begin&lt;br /&gt;
 VertexPointer    := VBOPointer;&lt;br /&gt;
&lt;br /&gt;
 VertexPointer^.X := GeneriertesVertex.X;&lt;br /&gt;
 VertexPointer^.Y := GeneriertesVertex.Y;&lt;br /&gt;
 VertexPointer^.Z := GeneriertesVertex.Z;&lt;br /&gt;
 VertexPointer^.S := GeneriertesVertex.S;&lt;br /&gt;
 VertexPointer^.T := GeneriertesVertex.T;&lt;br /&gt;
&lt;br /&gt;
 inc(Integer(VBOPointer), SizeOf(TVertex));&lt;br /&gt;
&lt;br /&gt;
 inc(VertexDatenLaenge);&lt;br /&gt;
 end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da sich dieses Tutorial (und v.&amp;amp;nbsp;a. diese Methode der Vertexdatenübergabe) an die fortgeschritteneren wendet, rede ich nicht lange um den heissen Brei herum. Wir durchlaufen also eine Schleife, in der wir unsere Vertexdaten dynamisch erstellen. Entweder in jedem Durchlauf, oder wir laden die Daten vor der Schleife z.&amp;amp;nbsp;B. aus einem 3D-Modell und entfernen sie danach aus dem Hauptspeicher. Erste Methode spart sowohl beim Programmstart, also auch während des Programmlaufes Arbeitsspeicher, während letztere dies nur während des Programmablaufs macht.&lt;br /&gt;
&lt;br /&gt;
Wir setzen die Adresse des Pointers der auf das momentan zu verändernde Vertex zeigt also auf die aktuelle Zeigerposition im VBO und schreiben dann unser Vertex direkt in den Grafikkartenspeicher. Ist dies getan, müssen wir die Adresse unseres VBOPointers natürlich um die Größe des gerade geschriebenen Vertexes erhöhen. Der Typecast auf ein Integer geschieht deshalb, weil man Pointer in Delphi nicht so einfach erhöhen oder verringern kann, Integers hingegen schon.&lt;br /&gt;
&lt;br /&gt;
Ich gehe mal davon aus,das meine Leserschaft mit obigem Codeschnippsel keinerlei Probleme hatte und bringe diese Methode der Übergabe von Vertexdaten an das VBO dann auch mit folgender Quellcodezeile zu Ende:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glUnMapBuffer(GL_ARRAY_BUFFER);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie unschwer zu erraten geben wir hier unser VBO für den weiteren Zugriff wieder frei. Dies ist nötig um dies später rendern zu können.&lt;br /&gt;
&lt;br /&gt;
==Übergabe der Vertexdaten (Variante 2: Über den Hauptspeicher)==&lt;br /&gt;
&lt;br /&gt;
Wem obige Variante nicht zugesagt hat, weil sie ihm zu komplex war, oder weil er seine Vertexdaten aus diversen Gründen im Hauptspeicher braucht, dem wird in diesem Abschnitt geholfen.&lt;br /&gt;
&lt;br /&gt;
Das Übergeben der Vertexdaten an das VBO über den Hauptspeicher gestaltet sich nämlich sehr einfach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBufferData(GL_ARRAY_BUFFER, SizeOf(VertexDaten), @VertexDaten, GL_STATIC_DRAW);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sieht einfach aus,hört sich einfach an und ist auch einfach! Statt wie im letzten Kapitel für die Vertexdaten einen Zeiger auf '''nil''' zu übergeben, übergeben wir OpenGL jetzt einen direkten Zeiger auf die Vertexdaten die im Hauptspeicher abgelegt sind. Die Daten werden dann zum VBO hochgeladen, und können anschliessen auch wieder aus dem Hauptspeicher entfernt werden.&lt;br /&gt;
&lt;br /&gt;
==Rendern des VBOs==&lt;br /&gt;
&lt;br /&gt;
Kommen wir nun also schon zum Ende unseres VBO-Tutorials. Dank der Einfachheit dieser Extension gabs halt einfach nicht mehr zu erklären. Das Zeichnen gestaltet sich nämlich OpenGL-typisch mehr als einfach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glInterleavedArrays(GL_T2F_V3F, SizeOf(TVertex), nil);&lt;br /&gt;
glDrawArrays(GL_QUADS, 0, VertexDatenLaenge);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schnell gezeichnet und schnell erklärt. Der erste Parameter gibt an, in welchem Format die Vertexdaten unseres VBOs abgelegt wurden (wir erinnern uns: '''GL_T2F_V3F'''), während der zweite Parameter den Speicherplatz, der zwischen zwei Vertices liegt, angibt. Würden wir ein einfaches Vertexarray zeichnen, so wäre der letzte Parameter ein Pointer auf die Vertexdaten im Hauptspeicher. Da wir allerdings vorher unser VBO gebunden haben, teilt hier ein '''nil''' mit, dass die vorher ins VBO geschriebenen Vertexdaten gerendert werden sollen.&lt;br /&gt;
&lt;br /&gt;
Mittels [[glDrawArrays]] sagen wir OpenGL dann noch, das es unsere VBO-Daten als Quads rendern soll.&lt;br /&gt;
&lt;br /&gt;
==Freigabe==&lt;br /&gt;
&lt;br /&gt;
Wie mit allen Objekten in der OpenGL ists auch keine schlechte Idee, unser VBO freizugeben,wenn es nicht mehr benötigt wird. Und obwohl beim Löschen des Renderkontextes solche Objekte vom Grafikkartentreiber freigegeben werden sollten, wäre es keine schlechte Idee des selbst zu erledigen,für den Fall das der Treiber da schlampig arbeitet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glDeleteBuffers(1, @VBO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Weitere Vertexformate==&lt;br /&gt;
Wem allein Texturkoordinaten und Vertices nicht reichen, sondern auch Farbangaben oder Vertexnormalen braucht, muss sich eines der folgenden Formate aussuchen. Die Konstantennamen sind logisch aufgebaut: ''GL_AngabeAnzahlTyp_AngabeAnzahlTyp...''&lt;br /&gt;
Als Angabe gibt es '''V'''ertex, '''T'''exturkoordinaten, '''N'''ormalen und '''C'''olor-Werte, also Farben.&lt;br /&gt;
Anzahl ist die Anzahl der Komponenten pro Angabe (V3 wären zum Bespiel die X, Y und Z-Koordinaten eines dreidimensionalen Vertex).&lt;br /&gt;
Typ ist entweder '''F'''loat oder '''U'''nsigned '''B'''yte. Die Vertexdaten müssen in der richtigen Reihenfolge mit den richtigen Typen an das VBO übergeben werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  GL_V2F&lt;br /&gt;
  GL_V3F&lt;br /&gt;
  GL_C4UB_V2F&lt;br /&gt;
  GL_C4UB_V3F&lt;br /&gt;
  GL_C3F_V3F           &lt;br /&gt;
  GL_N3F_V3F           &lt;br /&gt;
  GL_C4F_N3F_V3F       &lt;br /&gt;
  GL_T2F_V3F&lt;br /&gt;
  GL_T4F_V4F&lt;br /&gt;
  GL_T2F_C4UB_V3F&lt;br /&gt;
  GL_T2F_C3F_V3F&lt;br /&gt;
  GL_T2F_N3F_V3F      // Wohl eines der sinnvollsten Vertexformate. 2 Floats für Texturkoordinaten, 3 Floats für den Normalenvektor und 3 Floats für den eigentlichen Vertex.&lt;br /&gt;
  GL_T2F_C4F_N3F_V3F&lt;br /&gt;
  GL_T4F_C4F_N3F_V4F&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es ist sinnvoll, sich für jedes der Vertexformate, die man verwendet, einen Typen anzulegen, um später einfacher mit den VBOs arbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Falls man mit Shadern arbeitet kann man sich die Wahl des Vertexformates eigentlich sparen, denn hier kann man z.B. für jeden Attributtyp den man braucht (Vertex, Normale, Farbe, Normale, etc.) ein eigenes VBO anlegen und dieses dann entsprechend seinen Bedürfnissen im Shader interpretieren, oder einfach nur ein riesiges VBO nutzen und dort alle Daten nach Belieben ablegen und im Shader selbst interpretieren.&lt;br /&gt;
&lt;br /&gt;
==Nachwort==&lt;br /&gt;
&lt;br /&gt;
Das wars auch schon. Wie zu erkennen ist das Vertexbufferobjekt mal wieder eine gut durchdachte, recht nützliche und zudem auch weit nutzbare Erweiterung, die OpenGL technisch wieder einen Schritt weitergebracht hat.&lt;br /&gt;
&lt;br /&gt;
Wer mehr wissen will, der sollte sich unbedingt mal die passende [http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt Spezifikation] ansehen.&lt;br /&gt;
&lt;br /&gt;
Hoffe das Tutorial hat euch soviel Spaß gemacht wie mir das Verfassen des selbigen und ich hoffe auf reges Feedback!&lt;br /&gt;
&lt;br /&gt;
Euer&lt;br /&gt;
&lt;br /&gt;
Sascha Willems (webmaster_at_delphigl.de)&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[http://developer.nvidia.com/attach/6427 Nvidia: Using Vertex Buffer Objects]&lt;br /&gt;
&lt;br /&gt;
[http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt Spezifikation]&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_Cubemap]]|[[Tutorial_Vertexprogramme]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Vertexbuffer]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Vertexbufferobject&amp;diff=25084</id>
		<title>Tutorial Vertexbufferobject</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Vertexbufferobject&amp;diff=25084"/>
				<updated>2011-05-04T08:21:00Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Ave! */ &amp;quot;vor kurzem&amp;quot; ist nicht mehr ganz zutreffend&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=GL_ARB_Vertex_Buffer_Object=&lt;br /&gt;
==Ave!==&lt;br /&gt;
&lt;br /&gt;
Und willkommen zu meinem zweiten GL_ARB-Extension Tutorial. Diesmal beschäftigen wir uns mit der recht neuen '''GL_ARB_Vertex_Buffer_Object'''-Extension, die vom ARB am 12.Februar 2003 fertiggestellt und später im Jahr 2003 auch als Corefeature in OpenGL 1.5 promoted wurde.&lt;br /&gt;
&lt;br /&gt;
VBOs (Vertex Buffer Objects) sind quasi eine Erweiterung der recht weit verbreiteten Vertexarrays, die jedoch den großen Vorteil besitzen, dass ihre Vertexdaten serverseitig, also im schnellen VRAM der Grafikkarte, statt wie bei den VAs im Hauptspeicher, abgelegt werden. Das ist bei einer Displayliste zwar (fast) genauso der Fall, allerdings können die Daten nicht so einfach dynamisch geändert werden, was bei einem VBO aber bei Bedarf sehr einfach ist.&lt;br /&gt;
&lt;br /&gt;
Kurz gesagt vereint das VBO also die Flexibilität eines Vertexarrays mit der Geschwindigkeit einer Displayliste und eignet sich daher besonders für das Rendern aufwändiger Geometrie, egal ob statisch oder dynamisch.&lt;br /&gt;
&lt;br /&gt;
Eine weitere tolle Neuerung der VBO-API ist die Tatsache, dass man sich von OpenGL einen Pointer übergeben lassen kann, mit dessen Hilfe man Vertexdaten direkt in den VRAM schreiben kann. Dadurch spart man sich natürlich den Umweg über den Hauptspeicher und somit auch den erhöhten Speicherbedarf des Programmes beim Laden von Modellen o.&amp;amp;nbsp;Ä.&lt;br /&gt;
&lt;br /&gt;
VBOs bieten übrigens auch die Möglichkeit, sie je nach Anwendungsfall vom Grafikkartentreiber &amp;quot;optimieren&amp;quot; zu lassen. So legt man Vertexdaten, die im Nachhinein nicht mehr verändert werden sollenm am besten mit dem Schlüsselwort ''STATIC_DRAW'' ab, während dynamische Vertexdaten über ''DYNAMIC_DRAW'' abgelegt werden sollten. Doch dazu gibts später nähere Informationen.&lt;br /&gt;
&lt;br /&gt;
==Hardwareunterstützung==&lt;br /&gt;
&lt;br /&gt;
Inzwischen sollten alle gängigen Grafikkarten diese Extension problemlos unterstützen, ältere Modelle tun dies aber evtl. nur emuliert über den Treiber. Will man sein Programm auch für sehr alte Hardware (Karten älter als ATIs Radeon 9x00 bzw. NVidias GeForce 4-Serie) lauffähig machen, sollte man noch einen alternativen Renderpfad auf Basis von Displaylisten einbauen.&lt;br /&gt;
&lt;br /&gt;
==Erstellen des Vertexbufferobjects==&lt;br /&gt;
&lt;br /&gt;
Wie der Name vermuten lässt, handelt es sich beim VBO um ein Objekt, welches in Sachen Syntax an die anderen in OpenGL verfügbaren Objekte wie z.&amp;amp;nbsp;B. Texturenobjekte angelehnt ist. Das Erstellen eines VBOs geht daher genauso einfach von der Hand:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glGenBuffers(1, @VBO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun sollten wir in VBO eine gültige ID zurückgeliefert bekommen haben, welche ein Vertex buffer object referenziert.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun ein VBO erstellt haben, sollten wir dieses natürlich auch &amp;quot;aktivieren&amp;quot; (also binden), damit wir mit ihm arbeiten können:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBindBufferARB(GL_ARRAY_BUFFER, VBO);&lt;br /&gt;
glEnableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie erwähnt binden wir in der ersten Zeile unser frisch erstelltes VBO, während wir in der zweiten Zeile Vertexarrays auf der Clientseite aktivieren. Dies ist deshalb nötig, da wir das VBO später genauso zeichnen werden wie dies bei einem normalen VA der Fall wäre.&lt;br /&gt;
&lt;br /&gt;
==Übergabe der Vertexdaten (Variante 1: Ohne Umweg über den Hauptspeicher)==&lt;br /&gt;
&lt;br /&gt;
Wie in der Einleitung bereits erwähnt, bietet uns die VBO-API die Möglichkeit, unsere Vertexdaten ohne Umweg über den Hauptspeicher direkt in den Grafikkartenspeicher zu schreiben. Da dies eines der besten Features dieser neuen Extension ist, wollen wir uns auch zuerst mit dieser Übergabemethode beschäftigen.&lt;br /&gt;
&lt;br /&gt;
''Allerdings erstmal eine kleine Warnung vorweg: Pointer sind ein recht komplexes Thema, und ihre Nutzung erfordert einige Kenntnisse. Wer weder weiß, wie man damit umgeht, noch wie man sie einsetzt, sollte sich da zuerstmal schlau machen. Denn mit Pointer kann man ganz böse Sachen machen, wenn man sie nicht richtig nutzen kann!''&lt;br /&gt;
&lt;br /&gt;
Um unsere Vertexdaten als Pointer zu nutzen,müssen wir erstmal eine Vertexstruktur erstellen und auch einen passenden Pointertyp. Der Pointertyp ist zwar nicht zwingend (ein ^ täte es auch), aber macht die Sache übersichtlicher:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
PVertex = ^TVertex;&lt;br /&gt;
TVertex = packed record&lt;br /&gt;
 S,T,X,Y,Z : TGLFloat;&lt;br /&gt;
end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sicher fragen sich einige von euch jetzt, warum unser TVertex gerade so aussieht (also zuerst S,T und dann der Rest). Das liegt daran, dass wir unser VBO später über die Funktion [[glInterleavedArrays]] rendern werden, welche wissen will, wie unsere Vertexdaten im Speicher abgelegt sind. Unsere Vertexstruktur entspricht dabei dem Format ''GL_T2F_V3F''. ''T2F'' entspricht 2 Floats für die Texturenkoordinaten (S&amp;amp;T), gefolgt von ''V3F'' für die drei Floatwerte die unsere Vertexkoordinaten repräsentieren. Wenn ihr ein anderes Vertexformat verwendet, müsst ihr euch auch ein passendes Vertexformat aus der Spezifikation heraussuchen.&lt;br /&gt;
Ich verwende oben übrigens einen '''packed Record''' (auch wenn dies bei unserem Vertexformat nicht unbedingt ein Muss ist). Dies tue ich deshalb, da Delphi Werte bei einem normalen Record für einen beschleunigten Zugriff an einem (Double)Word-Raster ausrichtet und dies deshalb bei bestimmten Vertexformaten Probleme bereiten könnte.&lt;br /&gt;
&lt;br /&gt;
Hoffe mal das euch obiges klar ist,denn jetzt gehts ans Eingemachte. Wir kommen zum &amp;quot;schwersten&amp;quot; (ist ja ein relativer Begriff) Teil des Tutorials, nämlich der Übergabe der Vertexdaten an das VBO. Dazu müssen wir OpenGL zuerstmal mitteilen, wie groß unser VBO eigentlich sein soll,damit wir auch genug Platz im VRAM bekommen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBufferDataARB(GL_ARRAY_BUFFER, VertexAnzahl*SizeOf(TVertex), nil, GL_STATIC_DRAW);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schauen wir uns also mal die Parameter von [[glBufferData]] an. Zuerst müssen wir OpenGL mitteilen, was für einen Puffer wir erstellen wollen (in zukünftigen Versionen werden wohl noch andere folgen, z.&amp;amp;nbsp;B. Überbuffer). In unserem Falle also einen Arraypuffer. Parameter zwei übergibt dann den Speicherplatz, den wir reservieren wollen und ist recht selbsterklärend. Der dritte Parameter würde normalerweise einen Pointer auf unsere Vertexdaten enthalten. Da wir diese aber nicht im Hauptspeicher abgelegt haben, sondern über einen Pointer dort noch ablegen wollen, übergeben wir hier '''nil'''. OpenGL nimmt dies zur Kenntnis und spuckt dementsprechend auch keinen Fehler aus.&lt;br /&gt;
&lt;br /&gt;
Den letzten Parameter hab ich auch schon kurz in der Einführung angesprochen. Er teilt OpenGL mit, auf welchen Anwendungsfall unser VBO optimiert werden soll. Dabei gibt es folgende Anwendungsfälle,die ihr euch dann entsprechend eurer VBO-Verwendung aussuchen könnt:&lt;br /&gt;
&lt;br /&gt;
{|rules=&amp;quot;all&amp;quot; border = &amp;quot;1&amp;quot;&lt;br /&gt;
! STREAM_DRAW&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann eher selten als Quelle für OpenGL-Befehle genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STREAM_READ&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann selten von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! STREAM_COPY&lt;br /&gt;
| Die Vertexdaten werden einmal durch Kopieren von der GL übergeben und dann eher selten als Quelle für OpenGL-Befehle genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_DRAW&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_READ&lt;br /&gt;
| Die Vertexdaten werden einmal übergeben und dann recht oft von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! STATIC_COPY&lt;br /&gt;
| Die Vertexdaten werden einmal durch Kopieren von der GL übergeben und dann recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_DRAW&lt;br /&gt;
| Die Vertexdaten werden wiederholt angegeben (verändert) und recht oft von der Anwendung zum Rendern genutzt.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_READ&lt;br /&gt;
| Die Vertexdaten werden wiederholt durch das Auslesen von Daten durch OpenGL angegeben (verändert) und oft von der Anwendung angefordert.&lt;br /&gt;
|-&lt;br /&gt;
! DYNAMIC_COPY&lt;br /&gt;
| Die Vertexdaten werden wiederholt durch das Auslesen von Daten durch OpenGL angegeben (verändert) und recht oft zum Rendern genutzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beachtet werden sollte, dass es sich bei obigen Anwendungsmuster nur um Hinweise (Hints) handelt, die es auch in anderen Bereichen von OpenGL gibt. Diese Hinweise sind allerdings nicht verbindlich und werden von Treiber zu Treiber unterschiedlich behandelt.&lt;br /&gt;
&lt;br /&gt;
Nachdem OpenGL nun weiss was für ein VBO wir genau haben wollen, brauchen wir letztendlich nur noch einen Pointer, der &amp;quot;auf&amp;quot; das VBO zeigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
VBOPointer := glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion [[glMapBuffer]] liefert uns einen Pointer zurück, der den Speicherplatz des VBOs in den Adressraum des Clients &amp;quot;ummappt&amp;quot;, also in den Hauptspeicher. Dadurch wird es dann möglich, über eine Adresse im Hauptspeicher des Rechners direkt in den Grafikkartenspeicher zu schreiben. Der zweite Parameter gibt übrigens an, das wir in das VBO schreiben wollen. Der einzige weitere zulässige Parameter ist hier '''GL_READ_ONLY''', welcher zum Auslesen des VBOs dient.&lt;br /&gt;
&lt;br /&gt;
Endlich haben wir einen Pointer auf unser inzwischen auch im VRAM der Grafikkarte richtig dimensioniertes Vertexbufferobject, und sind nun in der Lage dort unsere Vertexdaten abzulegen. Was jetzt folgt ist ein wenig Pseudo-Code der diesen Vorgang darstellt. Pseudo-Code deshalb, weil die Vertexdaten die ins VBO kommen ja in jeder Anwendung unterschiedlich sind, und man so auch selbst ein wenig denken muss:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
VertexDatenLaenge := 0;&lt;br /&gt;
for i := 0 to High(MeineVertexDaten) do&lt;br /&gt;
 begin&lt;br /&gt;
 VertexPointer    := VBOPointer;&lt;br /&gt;
&lt;br /&gt;
 VertexPointer^.X := GeneriertesVertex.X;&lt;br /&gt;
 VertexPointer^.Y := GeneriertesVertex.Y;&lt;br /&gt;
 VertexPointer^.Z := GeneriertesVertex.Z;&lt;br /&gt;
 VertexPointer^.S := GeneriertesVertex.S;&lt;br /&gt;
 VertexPointer^.T := GeneriertesVertex.T;&lt;br /&gt;
&lt;br /&gt;
 inc(Integer(VBOPointer), SizeOf(TVertex));&lt;br /&gt;
&lt;br /&gt;
 inc(VertexDatenLaenge);&lt;br /&gt;
 end;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da sich dieses Tutorial (und v.&amp;amp;nbsp;a. diese Methode der Vertexdatenübergabe) an die fortgeschritteneren wendet, rede ich nicht lange um den heissen Brei herum. Wir durchlaufen also eine Schleife, in der wir unsere Vertexdaten dynamisch erstellen. Entweder in jedem Durchlauf, oder wir laden die Daten vor der Schleife z.&amp;amp;nbsp;B. aus einem 3D-Modell und entfernen sie danach aus dem Hauptspeicher. Erste Methode spart sowohl beim Programmstart, also auch während des Programmlaufes Arbeitsspeicher, während letztere dies nur während des Programmablaufs macht.&lt;br /&gt;
&lt;br /&gt;
Wir setzen die Adresse des Pointers der auf das momentan zu verändernde Vertex zeigt also auf die aktuelle Zeigerposition im VBO und schreiben dann unser Vertex direkt in den Grafikkartenspeicher. Ist dies getan, müssen wir die Adresse unseres VBOPointers natürlich um die Größe des gerade geschriebenen Vertexes erhöhen. Der Typecast auf ein Integer geschieht deshalb, weil man Pointer in Delphi nicht so einfach erhöhen oder verringern kann, Integers hingegen schon.&lt;br /&gt;
&lt;br /&gt;
Ich gehe mal davon aus,das meine Leserschaft mit obigem Codeschnippsel keinerlei Probleme hatte und bringe diese Methode der Übergabe von Vertexdaten an das VBO dann auch mit folgender Quellcodezeile zu Ende:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glUnMapBuffer(GL_ARRAY_BUFFER);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie unschwer zu erraten geben wir hier unser VBO für den weiteren Zugriff wieder frei. Dies ist nötig um dies später rendern zu können.&lt;br /&gt;
&lt;br /&gt;
==Übergabe der Vertexdaten (Variante 2: Über den Hauptspeicher)==&lt;br /&gt;
&lt;br /&gt;
Wem obige Variante nicht zugesagt hat, weil sie ihm zu komplex war, oder weil er seine Vertexdaten aus diversen Gründen im Hauptspeicher braucht, dem wird in diesem Abschnitt geholfen.&lt;br /&gt;
&lt;br /&gt;
Das Übergeben der Vertexdaten an das VBO über den Hauptspeicher gestaltet sich nämlich sehr einfach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBufferData(GL_ARRAY_BUFFER, SizeOf(VertexDaten), @VertexDaten, GL_STATIC_DRAW);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sieht einfach aus,hört sich einfach an und ist auch einfach! Statt wie im letzten Kapitel für die Vertexdaten einen Zeiger auf '''nil''' zu übergeben, übergeben wir OpenGL jetzt einen direkten Zeiger auf die Vertexdaten die im Hauptspeicher abgelegt sind. Die Daten werden dann zum VBO hochgeladen, und können anschliessen auch wieder aus dem Hauptspeicher entfernt werden.&lt;br /&gt;
&lt;br /&gt;
==Rendern des VBOs==&lt;br /&gt;
&lt;br /&gt;
Kommen wir nun also schon zum Ende unseres VBO-Tutorials. Dank der Einfachheit dieser Extension gabs halt einfach nicht mehr zu erklären. Das Zeichnen gestaltet sich nämlich OpenGL-typisch mehr als einfach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glInterleavedArrays(GL_T2F_V3F, SizeOf(TVertex), nil);&lt;br /&gt;
glDrawArrays(GL_QUADS, 0, VertexDatenLaenge);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schnell gezeichnet und schnell erklärt. Der erste Parameter gibt an, in welchem Format die Vertexdaten unseres VBOs abgelegt wurden (wir erinnern uns: '''GL_T2F_V3F'''), während der zweite Parameter den Speicherplatz, der zwischen zwei Vertices liegt, angibt. Würden wir ein einfaches Vertexarray zeichnen, so wäre der letzte Parameter ein Pointer auf die Vertexdaten im Hauptspeicher. Da wir allerdings vorher unser VBO gebunden haben, teilt hier ein '''nil''' mit, dass die vorher ins VBO geschriebenen Vertexdaten gerendert werden sollen.&lt;br /&gt;
&lt;br /&gt;
Mittels [[glDrawArrays]] sagen wir OpenGL dann noch, das es unsere VBO-Daten als Quads rendern soll.&lt;br /&gt;
&lt;br /&gt;
==Freigabe==&lt;br /&gt;
&lt;br /&gt;
Wie mit allen Objekten in der OpenGL ists auch keine schlechte Idee, unser VBO freizugeben,wenn es nicht mehr benötigt wird. Und obwohl beim Löschen des Renderkontextes solche Objekte vom Grafikkartentreiber freigegeben werden sollten, wäre es keine schlechte Idee des selbst zu erledigen,für den Fall das der Treiber da schlampig arbeitet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glDeleteBuffers(1, @VBO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Weitere Vertexformate==&lt;br /&gt;
Wem allein Texturkoordinaten und Vertices nicht reichen, sondern auch Farbangaben oder Vertexnormalen braucht, muss sich eines der folgenden Formate aussuchen. Die Konstantennamen sind logisch aufgebaut: ''GL_AngabeAnzahlTyp_AngabeAnzahlTyp...''&lt;br /&gt;
Als Angabe gibt es '''V'''ertex, '''T'''exturkoordinaten, '''N'''ormalen und '''C'''olor-Werte, also Farben.&lt;br /&gt;
Anzahl ist die Anzahl der Komponenten pro Angabe (V3 wären zum Bespiel die X, Y und Z-Koordinaten eines dreidimensionalen Vertex).&lt;br /&gt;
Typ ist entweder '''F'''loat oder '''U'''nsigned '''B'''yte. Die Vertexdaten müssen in der richtigen Reihenfolge mit den richtigen Typen an das VBO übergeben werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  GL_V2F&lt;br /&gt;
  GL_V3F&lt;br /&gt;
  GL_C4UB_V2F&lt;br /&gt;
  GL_C4UB_V3F&lt;br /&gt;
  GL_C3F_V3F           &lt;br /&gt;
  GL_N3F_V3F           &lt;br /&gt;
  GL_C4F_N3F_V3F       &lt;br /&gt;
  GL_T2F_V3F&lt;br /&gt;
  GL_T4F_V4F&lt;br /&gt;
  GL_T2F_C4UB_V3F&lt;br /&gt;
  GL_T2F_C3F_V3F&lt;br /&gt;
  GL_T2F_N3F_V3F      // Wohl eines der sinnvollsten Vertexformate. 2 Floats für Texturkoordinaten, 3 Floats für den Normalenvektor und 3 Floats für den eigentlichen Vertex.&lt;br /&gt;
  GL_T2F_C4F_N3F_V3F&lt;br /&gt;
  GL_T4F_C4F_N3F_V4F&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es ist sinnvoll, sich für jedes der Vertexformate, die man verwendet, einen Typen anzulegen, um später einfacher mit den VBOs arbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Falls man mit Shadern arbeitet kann man sich die Wahl des Vertexformates eigentlich sparen, denn hier kann man z.B. für jeden Attributtyp den man braucht (Vertex, Normale, Farbe, Normale, etc.) ein eigenes VBO anlegen und dieses dann entsprechend seinen Bedürfnissen im Shader interpretieren, oder einfach nur ein riesiges VBO nutzen und dort alle Daten nach Belieben ablegen und im Shader selbst interpretieren.&lt;br /&gt;
&lt;br /&gt;
==Nachwort==&lt;br /&gt;
&lt;br /&gt;
Das wars auch schon. Wie zu erkennen ist das Vertexbufferobjekt mal wieder eine gut durchdachte, recht nützliche und zudem auch weit nutzbare Erweiterung, die OpenGL technisch wieder einen Schritt weitergebracht hat.&lt;br /&gt;
&lt;br /&gt;
Wer mehr wissen will, der sollte sich unbedingt mal die passende [http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt Spezifikation] ansehen.&lt;br /&gt;
&lt;br /&gt;
Hoffe das Tutorial hat euch soviel Spaß gemacht wie mir das Verfassen des selbigen und ich hoffe auf reges Feedback!&lt;br /&gt;
&lt;br /&gt;
Euer&lt;br /&gt;
&lt;br /&gt;
Sascha Willems (webmaster_at_delphigl.de)&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[http://developer.nvidia.com/attach/6427 Nvidia: Using Vertex Buffer Objects]&lt;br /&gt;
&lt;br /&gt;
[http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt Spezifikation]&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_Cubemap]]|[[Tutorial_Vertexprogramme]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Vertexbuffer]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Benutzer:Coolcat&amp;diff=25033</id>
		<title>Benutzer:Coolcat</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Benutzer:Coolcat&amp;diff=25033"/>
				<updated>2011-01-30T16:38:58Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Realname: Martin Weusten&lt;br /&gt;
* Studium (abgeschlossen):&lt;br /&gt;
** Diplom Informatiker&lt;br /&gt;
** Vertiefungsrichtung: Computergrafik&lt;br /&gt;
** RWTH Aachen University&lt;br /&gt;
* Platform: Linux, C++&lt;br /&gt;
* Web: [http://martin-weusten.de martin-weusten.de]&lt;br /&gt;
* Jabber: http://jabber.rwth-aachen.de/w/images/c/ca/Jid-martin.weusten.jpg http://jabber.rwth-aachen.de/w/images/b/ba/Modrewrite-martin.weusten.gif&lt;br /&gt;
::(das ist '''keine''' EMailadresse, sondern eine [http://jabber.rwth-aachen.de/wiki/Jabber_-_Einfach_erkl%C3%A4rt! Jabberadresse!])&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Diskussion:glBlendColor&amp;diff=24998</id>
		<title>Diskussion:glBlendColor</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Diskussion:glBlendColor&amp;diff=24998"/>
				<updated>2011-01-02T21:03:13Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* GL_ARB_imaging Erweiterung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== GL_ARB_imaging Erweiterung ==&lt;br /&gt;
&lt;br /&gt;
glBlendColor ist doch bestimmt nicht nur Teil dieser Erweiterung, sondern auch Teil von OpenGL 2.0? Oder 2.1? Jedenfalls wäre es sonst sicher glBlendColorARB. Vielleicht kann das jemand mit Zeit mal checken... --[[Benutzer:Coolcat|Coolcat]] 22:01, 2. Jan. 2011 (CET)&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Diskussion:glBlendColor&amp;diff=24997</id>
		<title>Diskussion:glBlendColor</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Diskussion:glBlendColor&amp;diff=24997"/>
				<updated>2011-01-02T21:01:47Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: GL_ARB_imaging Erweiterung&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== GL_ARB_imaging Erweiterung ==&lt;br /&gt;
&lt;br /&gt;
glBlendColor ist doch bestimmt nicht nur Teil dieser Erweiterung, sondern auch Teil von OpenGL 2.0? Oder 2.1....vielleicht kann jemand mit Zeit das mal checken... --[[Benutzer:Coolcat|Coolcat]] 22:01, 2. Jan. 2011 (CET)&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:Formel_Clamp_To_Edge_Range.png&amp;diff=24984</id>
		<title>Datei:Formel Clamp To Edge Range.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:Formel_Clamp_To_Edge_Range.png&amp;diff=24984"/>
				<updated>2010-10-21T15:24:09Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: hat eine neue Version von „Datei:Formel Clamp To Edge Range.png“ hochgeladen:&amp;amp;#32;Vorzeichen korrigiert.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Bereich für GL_CLAMP_TO_EDGE ( glTexParameter ).&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=glPolygonOffset&amp;diff=24981</id>
		<title>glPolygonOffset</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=glPolygonOffset&amp;diff=24981"/>
				<updated>2010-10-18T11:37:16Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Siehe auch */ glDepthRange hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= glPolygonOffset =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Name ==&lt;br /&gt;
'''glPolygonOffset''' - setzt die Skalierung und Einheiten, die für die Tiefenberechnung verwendet werden sollen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Delphi-Spezifikation ==&lt;br /&gt;
 procedure '''glPolygonOffset'''(''factor'': TGLfloat; ''units'': TGLfloat);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameter ==&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; rules=&amp;quot;all&amp;quot;&lt;br /&gt;
! ''factor'' &lt;br /&gt;
| Definiert einen Skalierungsfaktor, der benutzt wird, um einen variablen Tiefenversatz (Depth Offset) für jedes Polygon.&amp;lt;br&amp;gt; Der '''Initialwert ist 0'''.&lt;br /&gt;
|-&lt;br /&gt;
! ''units'' &lt;br /&gt;
| Wird mit einem implementationsabhängigen Wert multipliziert, um einen konstanten Tiefenversatz zu erreichen.&amp;lt;br&amp;gt; Der '''Initialwert ist 0'''.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beschreibung == &lt;br /&gt;
Wenn '''GL_POLYGON_OFFSET_FILL''', '''GL_POLYGON_OFFSET_LINE''' oder '''GL_POLYGON_OFFSET_POINT''' aktiviert ist, wird der Tiefenwert jedes [[Fragment]]s um einen bestimmten Wert verschoben, nachdem der eigentliche Tiefenwert aus den betreffenden Vertexinformationen interpoliert wurde. Der Wert wird errechnet aus &amp;lt;br&amp;gt;&lt;br /&gt;
''factor'' * DZ + r * ''units'', wobei&amp;lt;br&amp;gt; &lt;br /&gt;
* DZ die Maßeinheit für die Tiefenverschiebung relativ zum Bildschirmbereich des Polygons und&lt;br /&gt;
* r der kleinste Wert ist, der garantiert noch eine gültige Verschiebung für die aktuelle OpenGL-Implementation liefert.&lt;br /&gt;
Der Offset wird addiert bevor der [[Tiefentest]] durchgeführt wird und bevor die Werte in den [[Tiefenpuffer]] geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
'''glPolygonOffset''' ist nützlich, um&lt;br /&gt;
* Raster-Bildern (engl: hidden-line images) zu rendern.&lt;br /&gt;
* [[Decals]] auf Oberflächen zu mappen.&lt;br /&gt;
* Solide Körper mit beleuchteten Kanten (highlighted edges) zu rendern.&lt;br /&gt;
* [[ZFighting]] zu beheben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Hinweise ==&lt;br /&gt;
'''glPolygonOffset''' ist ab OpenGL Version 1.1 verfügbar.&lt;br /&gt;
&lt;br /&gt;
'''glPolygonOffset''' hat keinen Effekt auf die Tiefenwerte im [[Feedbackpuffer]].&lt;br /&gt;
&lt;br /&gt;
'''glPolygonOffset''' hat keinen Effekt auf die [[Selektion]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Fehlermeldungen ==&lt;br /&gt;
'''GL_INVALID_OPERATION''' wird generiert, wenn '''glPolygonOffset''' innerhalb eines [[glBegin]]-[[glEnd]]-Blocks aufgerufen wird. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==  Zugehörige Wertrückgaben ==&lt;br /&gt;
[[glIsEnabled]] mit Token [[glIsEnabled#GL_POLYGON_OFFSET_FILL|GL_POLYGON_OFFSET_FILL]], [[glIsEnabled#GL_POLYGON_OFFSET_LINE|GL_POLYGON_OFFSET_LINE]], oder [[glIsEnabled#GL_POLYGON_OFFSET_POINT|GL_POLYGON_OFFSET_POINT]]&lt;br /&gt;
&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_POLYGON_OFFSET_FACTOR|GL_POLYGON_OFFSET_FACTOR]] oder [[glGet#GL_POLYGON_OFFSET_UNITS|GL_POLYGON_OFFSET_UNITS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
[[glDepthFunc]], [[glDepthRange]], [[glEnable]], [[glGet]], [[glIsEnabled]], [[glLineWidth]], [[glStencilOp]], [[glTexEnv]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:GL|PolygonOffset]]&lt;br /&gt;
[[Kategorie:GL1.1|PolygonOffset]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=glDepthRange&amp;diff=24980</id>
		<title>glDepthRange</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=glDepthRange&amp;diff=24980"/>
				<updated>2010-10-18T11:36:46Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Siehe auch */ glPolygonOffset hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= glDepthRange =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Name ==&lt;br /&gt;
'''glDepthRange''' - Bestimmt wie z-Werte von normalisierten Gerätekoordinaten (Device coordinates) zu Fensterkoordinaten (Window coordinates) umgerechnet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Delphi-Spezifikation ==&lt;br /&gt;
 procedure '''glDepthRange'''(''zNear'': TGLclampd; ''zFar'': TGLclampd);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameter ==&lt;br /&gt;
{| border=1 rules=all&lt;br /&gt;
! ''near''&lt;br /&gt;
| Beschreibt wie die vordere [[Clipping Plane|Schnittebene]] in Fensterkoordinaten umgerechnet werden soll.&amp;lt;br&amp;gt;&lt;br /&gt;
'''Vorgabewert''' ist 0.&lt;br /&gt;
|-&lt;br /&gt;
! ''far''&lt;br /&gt;
|Beschreibt, wie die hintere [[Clipping Plane|Schnittebene]] in Fensterkoordinaten umgerechnet werden soll.&amp;lt;br&amp;gt;&lt;br /&gt;
'''Vorgabewert''' ist 1.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beschreibung == &lt;br /&gt;
Nach dem die Z-Koordinaten abgeschnitten (clipping) und durch '''w''' geteilt wurden, reichen sie von -1.0 bis 1.0 im Bezug auf die vordere und hintere [[Clipping Plane|Schnittebene]].&amp;lt;br&amp;gt;&lt;br /&gt;
'''glDepthRange''' gibt an auf welchen Bereich der Fensterkoordinaten die Werte von -1.0 bis 1.0 linear abgebildet werden. Also wie aus dem Z-Wert eines Fragmentes, der an dieser Stelle im Bereich von -1.0 bis 1.0 liegt, der Wert für den DepthBuffer berechnet wird. Unabhängig von der tatsächlichen Implementierung darf dieser Wert im Intervall von 0.0 bis 1.0 liegen. Eine Z-Koordinate von -1.0 wird auf den Wert des Parameters near abgebildet und 1.0 entspricht den Wert des Parameters far. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hinweise ==&lt;br /&gt;
Es ist nicht notwendig, dass ''near'' kleiner als ''far'' ist. Umgekehrtes Mapping z.B. mit 1, 0 wird akzeptiert&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Früher hat man den DepthBuffer nur jeden zweiten Frame gelöscht, abwechselnd die Bereiche von 0.0 bis 0.5 und 1.0 bis 0.5 genutzt und die Richtung des DepthTests dementsprechend angepaßt. &lt;br /&gt;
Aufgrund von HW Optimierungen wie z.B. Hyper-Z usw. ist die Technik aber heutzutage nicht mehr angebracht und führt eher zu einem Geschwindigkeitsnachteil.&lt;br /&gt;
&lt;br /&gt;
== Fehlermeldungen ==&lt;br /&gt;
'''GL_INVALID_OPERATION''' wird generiert, wenn '''glDepthRange''' innerhalb eines [[glBegin]]-[[glEnd]]-Blocks aufgerufen wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==  Zugehörige Wertrückgaben ==&lt;br /&gt;
[[glGet]] mit Token [[glGet#GL_DEPTH_RANGE|GL_DEPTH_RANGE]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
[[glDepthFunc]], [[glPolygonOffset]], [[glViewport]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:GL|DepthRange]]&lt;br /&gt;
[[Kategorie:GL1.0|DepthRange]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=dglOpenGL.pas/en&amp;diff=24976</id>
		<title>dglOpenGL.pas/en</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=dglOpenGL.pas/en&amp;diff=24976"/>
				<updated>2010-10-09T09:20:21Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* The DelphiGL header */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Warnung| '''Delphi is already shipping with an OpenGL header, but that header is ''outdated and also buggy''. Therefore we ''urge everyone to NOT use that header, especially as it lacks most of OpenGL's current functionality.'''''}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;[[Bild:Flag_german.gif]][[dglOpenGL.pas| Gehe zur deutschen Version dieser Seite ]][[Bild:Flag_german.gif]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=The DelphiGL header=&lt;br /&gt;
So to compensate for the flaws of the outdated and buggy default header that is shipped with Delphi, the [http://www.delphigl.com (german) Delphi OpenGL Community] is providing it's own header that's permanently updated to provide steady support for new OpenGL features and functionality. This makes updating to a new header version easily (usually backwards compatibility is preserved), and it's even possible to use the header with other pascal compilers like Freepascal.&lt;br /&gt;
&lt;br /&gt;
It includes all OpenGL functions (currently up-to and including OpenGL 4.0) as well as all GLU ('''OpenGL Utility Libary''') functions and supports all ARB, EXT, NV and ATI extensions. It also includes additional, though lesser common extensions from other vendors like Apple, HP and SGI.&lt;br /&gt;
&lt;br /&gt;
A special feature of this header are boolean variables for each included extensions that are flagged upon initialisation (those boolean vars carry the same as their corresponding extensions), so you can quickly check wether an extension is support or not (instead of checking the strings returned by [[glGetString/en]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Licence/Terms of use==&lt;br /&gt;
The '''dglOpenGL.pas''' is distributed unter the terms and conditions of the '''Mozilla Public License Version 1.1'''. You can grab a copy of that licence [http://www.mozilla.org/MPL/MPL-1.1.html here].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;The contents of the dglOpenGL.pas are used with permission, subject to&amp;lt;br&amp;gt;&lt;br /&gt;
the Mozilla Public License Version 1.1 (the &amp;quot;License&amp;quot;); you may&amp;lt;br&amp;gt;&lt;br /&gt;
not use this file except in compliance with the License. You may&amp;lt;br&amp;gt;&lt;br /&gt;
obtain a copy of the License at&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mozilla.org/MPL/MPL-1.1.html&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Software distributed under the License is distributed on an&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;AS IS&amp;quot; basis, WITHOUT WARRANTY OF ANY KIND, either express or&amp;lt;br&amp;gt;&lt;br /&gt;
implied. See the License for the specific language governing&amp;lt;br&amp;gt;&lt;br /&gt;
rights and limitations under the License.&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==.NET support==&lt;br /&gt;
Due to lack of interest, .NET support has been removed as of header version 2.1!&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|The .Net header won't be developed any further, but reported errors may be fixed. So new OpenGL functionality can only be found in the normal header.}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Initializing function pointers (crucial)==&lt;br /&gt;
Along with the removal of .NET support, support for loading methods dynamically has also been removed and is no longer possible with the standard header. So it's crucial to call either '''AtivateRenderingContext''' or '''ReadExtensions''' alongside with '''ReadImplementationProperties''' in your application before accessing any of the OpenGL functions. Otherwise you'll likely be prompted with an access violation at address 0x00000000. &lt;br /&gt;
&lt;br /&gt;
''So if you get such an access violation please check if you called the above functions!''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Example for '''initializing''' the OpenGL function pointers (''needs to be called before you access any OpenGL functions'') :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;procedure OpenGLInit;&lt;br /&gt;
 begin&lt;br /&gt;
 InitOpenGL; // Don't forget, or first gl-Call will result in an access violation!&lt;br /&gt;
 MyDC := GetDC(...);&lt;br /&gt;
 MyRC := CreateRenderingContext(...);&lt;br /&gt;
 ActivateRenderingContext(MyDC, MyRC); // Necessary, will also load function pointers for all extension&lt;br /&gt;
 ...&lt;br /&gt;
 end;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Example for '''finishing''' the OpenGL render context (''should be called before quitting your application'') :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;procedure OpenGLFinish;&lt;br /&gt;
 begin&lt;br /&gt;
 DeactivateRenderingContext; // Deactivates the current context&lt;br /&gt;
 wglDeleteContext(myRC);&lt;br /&gt;
 ReleaseDC(Handle, myDC);&lt;br /&gt;
 end;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Download==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
'''{{ArchivLink|file=dglOpenGL|text=Current dglOpenGL.pas}} (Supports ''OpenGL 4.0'', works with Delphi 4 and up and Freepascal))'''&lt;br /&gt;
&lt;br /&gt;
'''{{ArchivLink|file=dglOpenGL_net|text=Last dglOpenGL.pas wit .NET support}} (Supports ''OpenGL 2.1'')'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''For Delphi 3 users :'''&lt;br /&gt;
''[[Benutzer:Mars|Mars]] has posted a version of the dglOpenGL.pas that will work with Delphi 3, you can get it here :&amp;lt;br&amp;gt;&lt;br /&gt;
[http://www.delphigl.com/forum/viewtopic.php?p=26697#25642 dglOpenGL.pas for Delphi 3] &amp;lt;br&amp;gt;&lt;br /&gt;
''(You need to be registered on the forums to download it)''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''There is also an inofficial version of this [http://www.delphigl.com/forum/viewtopic.php?t=4216 headers for C++].''&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
{{dglOpenGL_History}}&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Performance&amp;diff=24965</id>
		<title>Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Performance&amp;diff=24965"/>
				<updated>2010-09-22T15:07:28Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Quellen / Links */ Link aktualisiert, AMD hat das Dokument verschoben.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__&lt;br /&gt;
&lt;br /&gt;
Dieser Artikel liefert einige Tipps dazu wie man eine OpenGL-Anwendung optimieren kann. Einige der hier genannten Tipps schließen sich gegenseitig aus. Welche Vorgehensweise die beste ist hängt immer davon ab wo gerade der Engpass liegt. Es bringt zum Beispiel wenig den Shader zu optimieren, wenn eigentlich die zu langsame CPU das Problem ist.&lt;br /&gt;
&lt;br /&gt;
=== Grundlagen ===&lt;br /&gt;
Die folgenden Tipps sind eigentlich Pflicht, insbesondere da sie meist leicht zu realisieren sind. Beherzigt man diese überhaupt nicht, muss man sich nicht über eine unglaublich langsame Anwendung wundern. Die Reihenfolge gibt so ungefähr die Wichtigkeit an.&lt;br /&gt;
# Schließe nicht sichtbare Polygone so schnell wie möglich vom Rendering aus.&lt;br /&gt;
#* Aktiviere [[Backface Culling]].&lt;br /&gt;
#* Benutze [[Frustum Culling]].&lt;br /&gt;
#* Vor allem bei statischer Geometrie (die sich nicht bewegt) bieten sich die etablierten [[Techniken_und_Algorithmen#Raumunterteilungstechniken|Raumunterteilungstechniken]] wie BSP-Tree, Quad-Tree, usw. an.&lt;br /&gt;
# Vermeide Kommunikation zwischen CPU und GPU.&lt;br /&gt;
#* Lade nicht deine Texturen/Vertexdaten in jedem Frame neu auf die Grafikkarte hoch. Vermeide den Immediate Mode, also [[glBegin]]() und [[glEnd]](). Sofern du aber nur ein, zwei einzelne Dreiecke rendern möchtest ist der Immediate Mode akzeptabel.&lt;br /&gt;
#* Verwende [[VBO|Vertexbuffer-Objects]] oder [[Displayliste]]n.&lt;br /&gt;
# Verwende glDraw***-Aufrufe sparsam. Hiermit sind ALLE Befehle gemeint die irgendetwas rendern, z.B. [[glDrawArrays]], [[glDrawElements]], ..., aber auch [[glMultiDrawArrays]], usw...&lt;br /&gt;
#* Lieber ein paar Polygone mehr rendern, wenn du dadurch glDraw***-Aufrufe einsparen kannst.&lt;br /&gt;
#* Es spielt so gut wie keine Rolle, ob du 500 oder 1 Polygon renderst, da ein glDraw***-Aufrufe alleine schon ziemlich viel Zeit braucht.&lt;br /&gt;
# Optimiere deine Modelle. Nutze den Mesh-Optimierer in deiner 3D-Modellierungssoftware. Dies bringt zwar meist nicht viel, ist aber ja auch nicht mehr als ein Knopfdruck.&lt;br /&gt;
# Die Grafikkarte hat einen Cache für bereits vom Vertexshader verarbeitete Vertices. Dieser wird genutzt, wenn du [[Indices]] verwendest. Der Vertexshader muss also im optimalen Fall für jeden Vertex nur einmal ausgeführt werden, auch wenn der Vertex in mehreren Dreiecken verwendet wird. In jedem [[Mesh|geschlossenen]] und [[Mesh|2-mannigfaltigen]] Dreiecksnetz wird jeder Vertex im Durchschnitt von 6 Dreiecken verwendet. Hier lässt sich also so einiges an Speicher und Rechenleistung sparen.&lt;br /&gt;
# Nutze den Z-Buffer aus.&lt;br /&gt;
#* Sortiere nicht deine Polygone einzeln nach der Entfernung zur Kamera (Painters-Algorithm), sondern verwende den Z-Buffer.&lt;br /&gt;
#* Sofern du aufwendige Shader oder viele Texturen verwendest, rendere deine Objekte (nicht Polygone) von vorne nach hinten. Also grob sortieren und den Rest den Z-Buffer machen lassen.&lt;br /&gt;
#* Wenn du transparente Objekte hast, rendere zunächst von sortiert vorne nach hinten die undurchsichtigen Objekte. Die transparenten Objekte renderst du dann anschließend von hinten nach vorne.&lt;br /&gt;
# Vermeide wenn möglich häufige Shader-, Textur-, Material- und Statewechsel.&lt;br /&gt;
&lt;br /&gt;
===Performance Killer===&lt;br /&gt;
# Die OpenGL eigene Selektion wird nur schlecht als recht unterstützt. Eine [[Tutorial_ColorPicking|Color-Picking-Selection]] kann abhilfe leisten.&lt;br /&gt;
# [[glPushAttrib]] und glPopAttrib gelten laut [http://developer.amd.com/media/gpu_assets/GDC06-OpenGL_Tutorial_Day-Hart-OpenGL_03_Performance.pdf diesem ATI Dokument] als &amp;quot;State Evils&amp;quot;, welche sich negativ auf die Performance auswirken.&lt;br /&gt;
# Sofern die Extension [http://www.opengl.org/registry/specs/ARB/texture_non_power_of_two.txt GL_ARB_texture_non_power_of_two] nicht unterstützt wird, könnte eine Grafikkarte bei Texturen deren Kantenlänge keiner 2er-Potenz entspricht (also z.B. 256, 512, 1024, ...) in den Software-Modus umschalten. Im Software-Modus wird die Grafikkarte komplett von der CPU emuliert. Das dies nicht schnell sein kann dürfte klar sein.&lt;br /&gt;
&lt;br /&gt;
=== Fortgeschrittene Techniken ===&lt;br /&gt;
Die folgenden Tipps sind nicht so einfach zu realisieren oder erfordern Kenntnisse über fortgeschrittene Features wie [[Shader]], [[Instancing]]. Die Reihenfolge der Tipps ist hier nicht von Bedeutung.&lt;br /&gt;
# Ein glDraw*** wird nicht sofort ausgeführt, d.h. die CPU erhält die Kontrolle zurück bevor die GPU fertig mit rendern ist. Erst beim vertauschen von Front- und Backbuffer (&amp;quot;[[SwapBuffers]]&amp;quot;) oder einem expliziten [[glFinish]] wird synchronisiert. Folglich gebe zuerst der Grafikkarte was zu arbeiten, rechne dann deine Spiellogik, Physik, etc. auf der CPU und rufe dann erst [[SwapBuffers]] auf. Sowas geht natürlich nicht immer, aber man kann beispielsweise Physik und Rendern in zwei Frames aufspilten. Also du berechnest immer die Physik für das nächste Frame, während die GraKa das aktuelle Frame rendert. Siehe auch [[glFlush]].&lt;br /&gt;
# Überlege ob du Grafikspeicher sparen kannst indem du z.B. Texturkoordinaten oder Normalen zur Laufzeit im [[Shader]] berechnest.&amp;lt;br&amp;gt;(Beispiel: [[Shader#Beispiel:_Heightmap-Terrain|Heightmap-Terrain]])&lt;br /&gt;
# Wenn du viele identische Objekte renderst, überlege ob du [[Instancing]] einsetzen kannst. Erfordert allerdings halbwegs aktuelle Grafikhardware.&lt;br /&gt;
# Überlege ob du aufwendige Berechnungen, z.B. ein [[GLSL_Partikel_2|Partikelsystem]], nicht besser vollständig auf der Grafikkarte realisierst. Stichworte: [[FBO|Framebuffer-Objects]] und [[Transform-Feedback]].&lt;br /&gt;
# Verzichte auf einen [[Geometryshader]], wenn du ihn nicht unbedingt benötigst.&lt;br /&gt;
# Mit [[Triangulation|Triangle Strips]] lässt sich die Anzahl der notwendigen Vertices (bzw. Indices) bis auf ein Drittel reduzieren. Auch hier kommt der Vertexcache der Grafikkarte zum Einsatz.&lt;br /&gt;
# Überlege ob du deine Modelle immer in höchster Detailstufe ( mit Texturierung, Beleuchtung und anderen teuren Effekten ) zeichnen musst, wenn nicht, verwende [[LOD]] ( ''Level-Of-Detail'' ).&lt;br /&gt;
# Überlege ob du bei statischen Szenen komplexe Beleuchtungsberechnungen durch [[Lightmaps]] ersetzen kannst. Lightmaps benötigen zwar einiges an Grafikspeicher, jedoch müssen Berechnungen z.B. für Schatten nur einmal durchgeführt werden. Dies funktioniert sowohl bei [[GLSL_Licht_und_Schatten|Shadow Maps]] als auch bei [[Volumetrische_Stencilschatten|volumetrischen Stencilschatten]]. Auch [[Reflexion]]en oder [[Kaustik]]en stellen kein Problem dar.&lt;br /&gt;
&lt;br /&gt;
=== Shader ===&lt;br /&gt;
Die folgenden Tipps beschäftigen sich mit der Optimierung von [[Shader]]n. Auch hier ist die Reihenfolge nicht von Bedeutung.&lt;br /&gt;
# Vermeide den Universal-Shader. Shader-Wechsel sind zwar aufwendig, aber eine gigantische if-Verzweigung die für jeden Vertex und jeden Pixel (!) ausgeführt werden muss ist noch wesentlich aufwendiger. Optimiere deinen Shader für die Aufgabe die er erfüllen muss.&lt;br /&gt;
# Vermeide aufwendige Berechnungen im Shader. Möglicherweise ist es sinnvoll komplexe Berechnungen im voraus zu berechnen und im Shader eine Lookup-Textur zu verwenden. Sofern eine komplexe Berechnung nur von Uniform-Variablen abhängig ist, ist es oft sinnvoll einfach eine weitere Uniform-Variable zu spendieren und einmal auf der CPU zu berechnen.&lt;br /&gt;
# Verwende nach Möglichkeit die in GLSL [[Tutorial_glsl#Eingebaute_Funktionen|integrierten Funktionen]]. Diese Funktionen können zum Teil wesentlich schneller sein da sie zum Teil direkt in der Hardware implementiert sind.&lt;br /&gt;
# Optimiere deine Shader so weit wie möglich. Effizienz hat hier Vorrang vor der Lesbarkeit, da die Operationen möglicherweise millionenfach pro Frame ausgeführt werden. Im Fall der Fälle dann einfach mal etwas ausführlicher kommentieren. &lt;br /&gt;
# Versuche insbesondere aufwendige Operationen wie zum Beispiel eine Wurzel zu vermeiden. Bedenke das sich solche Operationen auch in eingebauten Funktionen wie beispielsweise {{INLINE_CODE|length}}, {{INLINE_CODE|distance}} und {{INLINE_CODE|normalize}} verstecken können.&lt;br /&gt;
# Vermeide Random-Access auf Texturen. Zwei nebeneinander liegende Texel einer Textur können üblicherweise schneller aus dem Speicher geladen werden als zwei Texel an völlig unterschiedlichen Positionen in der Textur. Nutze den GPU-Cache!&lt;br /&gt;
# Versuche nicht Speicher zu sparen in dem du Variablen im Shader zusammenfasst. Beispielsweise macht eine Variable {{INLINE_CODE|vec4 positionAndSize}} wenig Sinn, wenn Position und Größe nur wenig miteinander zu tun haben und du z.B. ständig mit {{INLINE_CODE|positionAndSize.xyz}} arbeitest. Verwende lieber separate Variablen, also {{INLINE_CODE|vec3 position}} und {{INLINE_CODE|float size}}. So kann der Compiler besser optimieren.&amp;lt;br&amp;gt;Dies gilt natürlich nur für lokale Variablen im Shader. Wenn du mit dieser Methode Werte in einer Textur zusammenfassen kannst ist dies natürlich sehr sinnvoll!&lt;br /&gt;
# Es gibt diverse Tools mit denen du feststellen kannst wo genau deine Anwendung die meiste Zeit benötigt bzw. wo du optimieren solltest.&lt;br /&gt;
#* [http://developer.nvidia.com/object/nvperfkit_home.html NVIDIA PerfKit]&lt;br /&gt;
#* [http://news.developer.nvidia.com/2006/12/nvidia_perfgrap.html NVIDIA PerfGraph]&lt;br /&gt;
#* [http://developer.amd.com/gpu/shader/Pages/default.aspx AMD GPU ShaderAnalyzer]&lt;br /&gt;
#* bitte ergänzen...&lt;br /&gt;
&lt;br /&gt;
=== Quellen / Links ===&lt;br /&gt;
* [http://developer.nvidia.com/object/gpu_programming_guide.html NVIDIA GPU Programming Guide]&lt;br /&gt;
* [http://developer.amd.com/media/gpu_assets/GDC06-OpenGL_Tutorial_Day-Hart-OpenGL_03_Performance.pdf OpenGL Performance Tuning, GDC 2006]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Performance&amp;diff=24964</id>
		<title>Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Performance&amp;diff=24964"/>
				<updated>2010-09-22T15:06:49Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Performance Killer */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__&lt;br /&gt;
&lt;br /&gt;
Dieser Artikel liefert einige Tipps dazu wie man eine OpenGL-Anwendung optimieren kann. Einige der hier genannten Tipps schließen sich gegenseitig aus. Welche Vorgehensweise die beste ist hängt immer davon ab wo gerade der Engpass liegt. Es bringt zum Beispiel wenig den Shader zu optimieren, wenn eigentlich die zu langsame CPU das Problem ist.&lt;br /&gt;
&lt;br /&gt;
=== Grundlagen ===&lt;br /&gt;
Die folgenden Tipps sind eigentlich Pflicht, insbesondere da sie meist leicht zu realisieren sind. Beherzigt man diese überhaupt nicht, muss man sich nicht über eine unglaublich langsame Anwendung wundern. Die Reihenfolge gibt so ungefähr die Wichtigkeit an.&lt;br /&gt;
# Schließe nicht sichtbare Polygone so schnell wie möglich vom Rendering aus.&lt;br /&gt;
#* Aktiviere [[Backface Culling]].&lt;br /&gt;
#* Benutze [[Frustum Culling]].&lt;br /&gt;
#* Vor allem bei statischer Geometrie (die sich nicht bewegt) bieten sich die etablierten [[Techniken_und_Algorithmen#Raumunterteilungstechniken|Raumunterteilungstechniken]] wie BSP-Tree, Quad-Tree, usw. an.&lt;br /&gt;
# Vermeide Kommunikation zwischen CPU und GPU.&lt;br /&gt;
#* Lade nicht deine Texturen/Vertexdaten in jedem Frame neu auf die Grafikkarte hoch. Vermeide den Immediate Mode, also [[glBegin]]() und [[glEnd]](). Sofern du aber nur ein, zwei einzelne Dreiecke rendern möchtest ist der Immediate Mode akzeptabel.&lt;br /&gt;
#* Verwende [[VBO|Vertexbuffer-Objects]] oder [[Displayliste]]n.&lt;br /&gt;
# Verwende glDraw***-Aufrufe sparsam. Hiermit sind ALLE Befehle gemeint die irgendetwas rendern, z.B. [[glDrawArrays]], [[glDrawElements]], ..., aber auch [[glMultiDrawArrays]], usw...&lt;br /&gt;
#* Lieber ein paar Polygone mehr rendern, wenn du dadurch glDraw***-Aufrufe einsparen kannst.&lt;br /&gt;
#* Es spielt so gut wie keine Rolle, ob du 500 oder 1 Polygon renderst, da ein glDraw***-Aufrufe alleine schon ziemlich viel Zeit braucht.&lt;br /&gt;
# Optimiere deine Modelle. Nutze den Mesh-Optimierer in deiner 3D-Modellierungssoftware. Dies bringt zwar meist nicht viel, ist aber ja auch nicht mehr als ein Knopfdruck.&lt;br /&gt;
# Die Grafikkarte hat einen Cache für bereits vom Vertexshader verarbeitete Vertices. Dieser wird genutzt, wenn du [[Indices]] verwendest. Der Vertexshader muss also im optimalen Fall für jeden Vertex nur einmal ausgeführt werden, auch wenn der Vertex in mehreren Dreiecken verwendet wird. In jedem [[Mesh|geschlossenen]] und [[Mesh|2-mannigfaltigen]] Dreiecksnetz wird jeder Vertex im Durchschnitt von 6 Dreiecken verwendet. Hier lässt sich also so einiges an Speicher und Rechenleistung sparen.&lt;br /&gt;
# Nutze den Z-Buffer aus.&lt;br /&gt;
#* Sortiere nicht deine Polygone einzeln nach der Entfernung zur Kamera (Painters-Algorithm), sondern verwende den Z-Buffer.&lt;br /&gt;
#* Sofern du aufwendige Shader oder viele Texturen verwendest, rendere deine Objekte (nicht Polygone) von vorne nach hinten. Also grob sortieren und den Rest den Z-Buffer machen lassen.&lt;br /&gt;
#* Wenn du transparente Objekte hast, rendere zunächst von sortiert vorne nach hinten die undurchsichtigen Objekte. Die transparenten Objekte renderst du dann anschließend von hinten nach vorne.&lt;br /&gt;
# Vermeide wenn möglich häufige Shader-, Textur-, Material- und Statewechsel.&lt;br /&gt;
&lt;br /&gt;
===Performance Killer===&lt;br /&gt;
# Die OpenGL eigene Selektion wird nur schlecht als recht unterstützt. Eine [[Tutorial_ColorPicking|Color-Picking-Selection]] kann abhilfe leisten.&lt;br /&gt;
# [[glPushAttrib]] und glPopAttrib gelten laut [http://developer.amd.com/media/gpu_assets/GDC06-OpenGL_Tutorial_Day-Hart-OpenGL_03_Performance.pdf diesem ATI Dokument] als &amp;quot;State Evils&amp;quot;, welche sich negativ auf die Performance auswirken.&lt;br /&gt;
# Sofern die Extension [http://www.opengl.org/registry/specs/ARB/texture_non_power_of_two.txt GL_ARB_texture_non_power_of_two] nicht unterstützt wird, könnte eine Grafikkarte bei Texturen deren Kantenlänge keiner 2er-Potenz entspricht (also z.B. 256, 512, 1024, ...) in den Software-Modus umschalten. Im Software-Modus wird die Grafikkarte komplett von der CPU emuliert. Das dies nicht schnell sein kann dürfte klar sein.&lt;br /&gt;
&lt;br /&gt;
=== Fortgeschrittene Techniken ===&lt;br /&gt;
Die folgenden Tipps sind nicht so einfach zu realisieren oder erfordern Kenntnisse über fortgeschrittene Features wie [[Shader]], [[Instancing]]. Die Reihenfolge der Tipps ist hier nicht von Bedeutung.&lt;br /&gt;
# Ein glDraw*** wird nicht sofort ausgeführt, d.h. die CPU erhält die Kontrolle zurück bevor die GPU fertig mit rendern ist. Erst beim vertauschen von Front- und Backbuffer (&amp;quot;[[SwapBuffers]]&amp;quot;) oder einem expliziten [[glFinish]] wird synchronisiert. Folglich gebe zuerst der Grafikkarte was zu arbeiten, rechne dann deine Spiellogik, Physik, etc. auf der CPU und rufe dann erst [[SwapBuffers]] auf. Sowas geht natürlich nicht immer, aber man kann beispielsweise Physik und Rendern in zwei Frames aufspilten. Also du berechnest immer die Physik für das nächste Frame, während die GraKa das aktuelle Frame rendert. Siehe auch [[glFlush]].&lt;br /&gt;
# Überlege ob du Grafikspeicher sparen kannst indem du z.B. Texturkoordinaten oder Normalen zur Laufzeit im [[Shader]] berechnest.&amp;lt;br&amp;gt;(Beispiel: [[Shader#Beispiel:_Heightmap-Terrain|Heightmap-Terrain]])&lt;br /&gt;
# Wenn du viele identische Objekte renderst, überlege ob du [[Instancing]] einsetzen kannst. Erfordert allerdings halbwegs aktuelle Grafikhardware.&lt;br /&gt;
# Überlege ob du aufwendige Berechnungen, z.B. ein [[GLSL_Partikel_2|Partikelsystem]], nicht besser vollständig auf der Grafikkarte realisierst. Stichworte: [[FBO|Framebuffer-Objects]] und [[Transform-Feedback]].&lt;br /&gt;
# Verzichte auf einen [[Geometryshader]], wenn du ihn nicht unbedingt benötigst.&lt;br /&gt;
# Mit [[Triangulation|Triangle Strips]] lässt sich die Anzahl der notwendigen Vertices (bzw. Indices) bis auf ein Drittel reduzieren. Auch hier kommt der Vertexcache der Grafikkarte zum Einsatz.&lt;br /&gt;
# Überlege ob du deine Modelle immer in höchster Detailstufe ( mit Texturierung, Beleuchtung und anderen teuren Effekten ) zeichnen musst, wenn nicht, verwende [[LOD]] ( ''Level-Of-Detail'' ).&lt;br /&gt;
# Überlege ob du bei statischen Szenen komplexe Beleuchtungsberechnungen durch [[Lightmaps]] ersetzen kannst. Lightmaps benötigen zwar einiges an Grafikspeicher, jedoch müssen Berechnungen z.B. für Schatten nur einmal durchgeführt werden. Dies funktioniert sowohl bei [[GLSL_Licht_und_Schatten|Shadow Maps]] als auch bei [[Volumetrische_Stencilschatten|volumetrischen Stencilschatten]]. Auch [[Reflexion]]en oder [[Kaustik]]en stellen kein Problem dar.&lt;br /&gt;
&lt;br /&gt;
=== Shader ===&lt;br /&gt;
Die folgenden Tipps beschäftigen sich mit der Optimierung von [[Shader]]n. Auch hier ist die Reihenfolge nicht von Bedeutung.&lt;br /&gt;
# Vermeide den Universal-Shader. Shader-Wechsel sind zwar aufwendig, aber eine gigantische if-Verzweigung die für jeden Vertex und jeden Pixel (!) ausgeführt werden muss ist noch wesentlich aufwendiger. Optimiere deinen Shader für die Aufgabe die er erfüllen muss.&lt;br /&gt;
# Vermeide aufwendige Berechnungen im Shader. Möglicherweise ist es sinnvoll komplexe Berechnungen im voraus zu berechnen und im Shader eine Lookup-Textur zu verwenden. Sofern eine komplexe Berechnung nur von Uniform-Variablen abhängig ist, ist es oft sinnvoll einfach eine weitere Uniform-Variable zu spendieren und einmal auf der CPU zu berechnen.&lt;br /&gt;
# Verwende nach Möglichkeit die in GLSL [[Tutorial_glsl#Eingebaute_Funktionen|integrierten Funktionen]]. Diese Funktionen können zum Teil wesentlich schneller sein da sie zum Teil direkt in der Hardware implementiert sind.&lt;br /&gt;
# Optimiere deine Shader so weit wie möglich. Effizienz hat hier Vorrang vor der Lesbarkeit, da die Operationen möglicherweise millionenfach pro Frame ausgeführt werden. Im Fall der Fälle dann einfach mal etwas ausführlicher kommentieren. &lt;br /&gt;
# Versuche insbesondere aufwendige Operationen wie zum Beispiel eine Wurzel zu vermeiden. Bedenke das sich solche Operationen auch in eingebauten Funktionen wie beispielsweise {{INLINE_CODE|length}}, {{INLINE_CODE|distance}} und {{INLINE_CODE|normalize}} verstecken können.&lt;br /&gt;
# Vermeide Random-Access auf Texturen. Zwei nebeneinander liegende Texel einer Textur können üblicherweise schneller aus dem Speicher geladen werden als zwei Texel an völlig unterschiedlichen Positionen in der Textur. Nutze den GPU-Cache!&lt;br /&gt;
# Versuche nicht Speicher zu sparen in dem du Variablen im Shader zusammenfasst. Beispielsweise macht eine Variable {{INLINE_CODE|vec4 positionAndSize}} wenig Sinn, wenn Position und Größe nur wenig miteinander zu tun haben und du z.B. ständig mit {{INLINE_CODE|positionAndSize.xyz}} arbeitest. Verwende lieber separate Variablen, also {{INLINE_CODE|vec3 position}} und {{INLINE_CODE|float size}}. So kann der Compiler besser optimieren.&amp;lt;br&amp;gt;Dies gilt natürlich nur für lokale Variablen im Shader. Wenn du mit dieser Methode Werte in einer Textur zusammenfassen kannst ist dies natürlich sehr sinnvoll!&lt;br /&gt;
# Es gibt diverse Tools mit denen du feststellen kannst wo genau deine Anwendung die meiste Zeit benötigt bzw. wo du optimieren solltest.&lt;br /&gt;
#* [http://developer.nvidia.com/object/nvperfkit_home.html NVIDIA PerfKit]&lt;br /&gt;
#* [http://news.developer.nvidia.com/2006/12/nvidia_perfgrap.html NVIDIA PerfGraph]&lt;br /&gt;
#* [http://developer.amd.com/gpu/shader/Pages/default.aspx AMD GPU ShaderAnalyzer]&lt;br /&gt;
#* bitte ergänzen...&lt;br /&gt;
&lt;br /&gt;
=== Quellen / Links ===&lt;br /&gt;
* [http://developer.nvidia.com/object/gpu_programming_guide.html NVIDIA GPU Programming Guide]&lt;br /&gt;
* [http://ati.amd.com/developer/gdc/2006/GDC06-OpenGL_Tutorial_Day-Hart-OpenGL_03_Performance.pdf OpenGL Performance Tuning, GDC 2006]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Model_Loader&amp;diff=24809</id>
		<title>Model Loader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Model_Loader&amp;diff=24809"/>
				<updated>2010-04-04T09:41:15Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Format und Model Loader Liste */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Was ist das?==&lt;br /&gt;
Ein Modell-Loader ist eine Unit, oder DLL die ein 3D-Model in einem bestimmten Typ* vorliegt für ein Programm oder für was auch immer lädt, damit es angezeigt werden kann.&lt;br /&gt;
&lt;br /&gt;
==Für was brauch ich das?==&lt;br /&gt;
Wenn man 3D Modelle von speziellen Objekten (z.B. Einheiten, Landschaftsgegenstände und Gebäude in Strategiespiele, oder Gegner für Shooter) benötigt, ist es wesentlich komfortabler ein Modell in einem speziellen 3D Modellierungsprogramm zu modellieren, als es „von Hand“ in OpenGL zu bauen. Man muss allerdings anschließend die Daten (Informationen zur Geometrie, Texturierung eventl. Animierung) selbst aus den abgespeicherten Modellformaten extrahieren.&lt;br /&gt;
&lt;br /&gt;
Und genau da kommt der „Model Loader“ ins Spiel. Dieses Stück Code ermöglicht es dem Nutzer die Daten komfortabel aus den Dateien zu Laden. Das erleichtert die Arbeit im Vergleich zu selber gebauten Objekten natürlich enorm. Und die Modelle sehen, wenn man genügend Talent hat, auch meist wesentlich besser aus.&lt;br /&gt;
&lt;br /&gt;
==Speichern alle Formate Animationen?==&lt;br /&gt;
Nein! Es sind sogar nur recht wenige die Animationen bieten. Außerdem gibt es Modeller die Animationen nicht in einer Datei sondern in vielen Einzeldateien speichern. Allerdings heißt das nicht, dass nur die Modelle aus teuren Modellern Animationen (vernünftig) speichern können. Es gibt auch freie und günstige Software (Milkshape 3D), die so etwas kann.&lt;br /&gt;
&lt;br /&gt;
==Welche Modellformate gibt es?==&lt;br /&gt;
Hier eine kleine Auflistung der gebräuchlichsten Formate:&lt;br /&gt;
* .blend ([http://blender3d.org Blender])&lt;br /&gt;
* .collada ([http://www.khronos.org/collada/ Collada])&lt;br /&gt;
* .3ds (3D Studio)&lt;br /&gt;
* .lwo (Lightwave)&lt;br /&gt;
* .ms3d (Milkshape 3D)&lt;br /&gt;
* .bsp (ID Software BSP)&lt;br /&gt;
* .md3 (ID Software MD3)&lt;br /&gt;
* .x (Microsoft DirectX)&lt;br /&gt;
* .off (Object File Format)&lt;br /&gt;
&lt;br /&gt;
==Wozu gibt es Exporter Plugins und Scripte?==&lt;br /&gt;
Für 3D Entwicker ist es wichtig, die erstellten Meshdaten auch in ein vorhandenes oder eigenes Format zu speichern.&lt;br /&gt;
Hierzu gibt es von den meisten 3D Editioren Plugins oder Scripte die dieses erledigen.&lt;br /&gt;
&lt;br /&gt;
Einige Programme wie 3DS Max und Blender bieten beides, wobei in Fall von 3DS Max lieber Plugins verwendet werden sollten (Geschwindigkeitsvorteil). Blender bietet für Scripte einen Python Interpreter, der lediglich ein bischen modifiziert wurde.&lt;br /&gt;
&lt;br /&gt;
Mit der Möglichkeit, eigene Format zu speichern, kann man sich auch aus dem Lizensproblem für bestimmte Formate rauswinden und man hat die Möglichkeit das Format an benötigte Bedingungen anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Format und Model Loader Liste ==&lt;br /&gt;
{|{{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;10%&amp;quot;| Format&lt;br /&gt;
!width=&amp;quot;40%&amp;quot;| Format Features&lt;br /&gt;
!width=&amp;quot;50%&amp;quot;| Loader&lt;br /&gt;
|-&lt;br /&gt;
| Cal3D&lt;br /&gt;
|&lt;br /&gt;
* kombinieren von Animationen (30%&amp;amp;nbsp;gehen, 50%&amp;amp;nbsp;rennen, 20%&amp;amp;nbsp;stolzieren und Winken)&lt;br /&gt;
* stufenloses [[Level of Detail]]&lt;br /&gt;
|&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Name&lt;br /&gt;
! nutzbar mit&lt;br /&gt;
! Umgesetzte Features&lt;br /&gt;
|-&lt;br /&gt;
| [http://gna.org/projects/cal3d/ Cal3D]&lt;br /&gt;
| C++&lt;br /&gt;
| alle Cal3D&amp;amp;nbsp;(0.10) Features&lt;br /&gt;
|-&lt;br /&gt;
| DCal3D&lt;br /&gt;
| Delphi / Object Pascal&lt;br /&gt;
| nur Cal3D&amp;amp;nbsp;0.6 Features&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 3DS&lt;br /&gt;
|&lt;br /&gt;
* Meshbasierendes 3D-Format&lt;br /&gt;
* Vertexbasierende Animationen via Keyframes&lt;br /&gt;
* Normalen werden indirekt über Smoothinggroups gesichert&lt;br /&gt;
'''Format stammt noch aus DOS-zeiten, daher diverse Einschränkungen :'''&lt;br /&gt;
* Maximal 65k Vertices pro [[Mesh]]&lt;br /&gt;
* Texturnamen sind auf [http://de.wikipedia.org/wiki/8.3 8.3 Dateinamen] beschränkt&lt;br /&gt;
* Bones werden nicht exportiert&lt;br /&gt;
* Normalen werden nicht exportiert (nur Smoothinggroups)&lt;br /&gt;
'''Kein freies Format'''&lt;br /&gt;
|&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Name&lt;br /&gt;
! nutzbar mit&lt;br /&gt;
! Umgesetzte Features&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.delphigl.com/forum/viewtopic.php?t=2421 gl3ds]&lt;br /&gt;
| Delphi / Object Pascal&lt;br /&gt;
| Unbekannt&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.lib3ds.org/ lib3ds]&lt;br /&gt;
| C/C++&lt;br /&gt;
| Unbekannt&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| OFF&lt;br /&gt;
|&lt;br /&gt;
* extrem einfaches 3D-Format&lt;br /&gt;
* ASCII und Binär-Variante&lt;br /&gt;
* nur Vertices, Normalen, Texturkoordinaten und Farben&lt;br /&gt;
* nicht auf Dreiecke beschränkt, beliebige Polygone möglich.&lt;br /&gt;
* '''nicht jeder Exporter (z.B. Blender) kann das vollständige Format!'''&lt;br /&gt;
| Da das Format so einfach ist ist ein Loader schnell implementiert:&lt;br /&gt;
[http://web.mit.edu/lugao/MacData/afs/athena/software/geomview_v1.9.3/www/OFF.html Formatbeschreibung].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Technik_oder_Algorithmus]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Model_Loader&amp;diff=24808</id>
		<title>Model Loader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Model_Loader&amp;diff=24808"/>
				<updated>2010-04-04T09:33:06Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Welche Modellformate gibt es? */ OFF-Format&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Was ist das?==&lt;br /&gt;
Ein Modell-Loader ist eine Unit, oder DLL die ein 3D-Model in einem bestimmten Typ* vorliegt für ein Programm oder für was auch immer lädt, damit es angezeigt werden kann.&lt;br /&gt;
&lt;br /&gt;
==Für was brauch ich das?==&lt;br /&gt;
Wenn man 3D Modelle von speziellen Objekten (z.B. Einheiten, Landschaftsgegenstände und Gebäude in Strategiespiele, oder Gegner für Shooter) benötigt, ist es wesentlich komfortabler ein Modell in einem speziellen 3D Modellierungsprogramm zu modellieren, als es „von Hand“ in OpenGL zu bauen. Man muss allerdings anschließend die Daten (Informationen zur Geometrie, Texturierung eventl. Animierung) selbst aus den abgespeicherten Modellformaten extrahieren.&lt;br /&gt;
&lt;br /&gt;
Und genau da kommt der „Model Loader“ ins Spiel. Dieses Stück Code ermöglicht es dem Nutzer die Daten komfortabel aus den Dateien zu Laden. Das erleichtert die Arbeit im Vergleich zu selber gebauten Objekten natürlich enorm. Und die Modelle sehen, wenn man genügend Talent hat, auch meist wesentlich besser aus.&lt;br /&gt;
&lt;br /&gt;
==Speichern alle Formate Animationen?==&lt;br /&gt;
Nein! Es sind sogar nur recht wenige die Animationen bieten. Außerdem gibt es Modeller die Animationen nicht in einer Datei sondern in vielen Einzeldateien speichern. Allerdings heißt das nicht, dass nur die Modelle aus teuren Modellern Animationen (vernünftig) speichern können. Es gibt auch freie und günstige Software (Milkshape 3D), die so etwas kann.&lt;br /&gt;
&lt;br /&gt;
==Welche Modellformate gibt es?==&lt;br /&gt;
Hier eine kleine Auflistung der gebräuchlichsten Formate:&lt;br /&gt;
* .blend ([http://blender3d.org Blender])&lt;br /&gt;
* .collada ([http://www.khronos.org/collada/ Collada])&lt;br /&gt;
* .3ds (3D Studio)&lt;br /&gt;
* .lwo (Lightwave)&lt;br /&gt;
* .ms3d (Milkshape 3D)&lt;br /&gt;
* .bsp (ID Software BSP)&lt;br /&gt;
* .md3 (ID Software MD3)&lt;br /&gt;
* .x (Microsoft DirectX)&lt;br /&gt;
* .off (Object File Format)&lt;br /&gt;
&lt;br /&gt;
==Wozu gibt es Exporter Plugins und Scripte?==&lt;br /&gt;
Für 3D Entwicker ist es wichtig, die erstellten Meshdaten auch in ein vorhandenes oder eigenes Format zu speichern.&lt;br /&gt;
Hierzu gibt es von den meisten 3D Editioren Plugins oder Scripte die dieses erledigen.&lt;br /&gt;
&lt;br /&gt;
Einige Programme wie 3DS Max und Blender bieten beides, wobei in Fall von 3DS Max lieber Plugins verwendet werden sollten (Geschwindigkeitsvorteil). Blender bietet für Scripte einen Python Interpreter, der lediglich ein bischen modifiziert wurde.&lt;br /&gt;
&lt;br /&gt;
Mit der Möglichkeit, eigene Format zu speichern, kann man sich auch aus dem Lizensproblem für bestimmte Formate rauswinden und man hat die Möglichkeit das Format an benötigte Bedingungen anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Format und Model Loader Liste ==&lt;br /&gt;
{|{{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;10%&amp;quot;| Format&lt;br /&gt;
!width=&amp;quot;40%&amp;quot;| Format Features&lt;br /&gt;
!width=&amp;quot;50%&amp;quot;| Loader&lt;br /&gt;
|-&lt;br /&gt;
| Cal3D&lt;br /&gt;
|&lt;br /&gt;
* kombinieren von Animationen (30%&amp;amp;nbsp;gehen, 50%&amp;amp;nbsp;rennen, 20%&amp;amp;nbsp;stolzieren und Winken)&lt;br /&gt;
* stufenloses [[Level of Detail]]&lt;br /&gt;
|&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Name&lt;br /&gt;
! nutzbar mit&lt;br /&gt;
! Umgesetzte Features&lt;br /&gt;
|-&lt;br /&gt;
| [http://gna.org/projects/cal3d/ Cal3D]&lt;br /&gt;
| C++&lt;br /&gt;
| alle Cal3D&amp;amp;nbsp;(0.10) Features&lt;br /&gt;
|-&lt;br /&gt;
| DCal3D&lt;br /&gt;
| Delphi / Object Pascal&lt;br /&gt;
| nur Cal3D&amp;amp;nbsp;0.6 Features&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 3DS&lt;br /&gt;
|&lt;br /&gt;
* Meshbasierendes 3D-Format&lt;br /&gt;
* Vertexbasierende Animationen via Keyframes&lt;br /&gt;
* Normalen werden indirekt über Smoothinggroups gesichert&lt;br /&gt;
'''Format stammt noch aus DOS-zeiten, daher diverse Einschränkungen :'''&lt;br /&gt;
* Maximal 65k Vertices pro [[Mesh]]&lt;br /&gt;
* Texturnamen sind auf [http://de.wikipedia.org/wiki/8.3 8.3 Dateinamen] beschränkt&lt;br /&gt;
* Bones werden nicht exportiert&lt;br /&gt;
* Normalen werden nicht exportiert (nur Smoothinggroups)&lt;br /&gt;
'''Kein freies Format'''&lt;br /&gt;
|&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Name&lt;br /&gt;
! nutzbar mit&lt;br /&gt;
! Umgesetzte Features&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.delphigl.com/forum/viewtopic.php?t=2421 gl3ds]&lt;br /&gt;
| Delphi / Object Pascal&lt;br /&gt;
| Unbekannt&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.lib3ds.org/ lib3ds]&lt;br /&gt;
| C/C++&lt;br /&gt;
| Unbekannt&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Technik_oder_Algorithmus]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Model_Loader&amp;diff=24807</id>
		<title>Model Loader</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Model_Loader&amp;diff=24807"/>
				<updated>2010-04-04T09:20:56Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Format und Model Loader Liste */ Cal3D hat scheinbar eine neue Website&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Was ist das?==&lt;br /&gt;
Ein Modell-Loader ist eine Unit, oder DLL die ein 3D-Model in einem bestimmten Typ* vorliegt für ein Programm oder für was auch immer lädt, damit es angezeigt werden kann.&lt;br /&gt;
&lt;br /&gt;
==Für was brauch ich das?==&lt;br /&gt;
Wenn man 3D Modelle von speziellen Objekten (z.B. Einheiten, Landschaftsgegenstände und Gebäude in Strategiespiele, oder Gegner für Shooter) benötigt, ist es wesentlich komfortabler ein Modell in einem speziellen 3D Modellierungsprogramm zu modellieren, als es „von Hand“ in OpenGL zu bauen. Man muss allerdings anschließend die Daten (Informationen zur Geometrie, Texturierung eventl. Animierung) selbst aus den abgespeicherten Modellformaten extrahieren.&lt;br /&gt;
&lt;br /&gt;
Und genau da kommt der „Model Loader“ ins Spiel. Dieses Stück Code ermöglicht es dem Nutzer die Daten komfortabel aus den Dateien zu Laden. Das erleichtert die Arbeit im Vergleich zu selber gebauten Objekten natürlich enorm. Und die Modelle sehen, wenn man genügend Talent hat, auch meist wesentlich besser aus.&lt;br /&gt;
&lt;br /&gt;
==Speichern alle Formate Animationen?==&lt;br /&gt;
Nein! Es sind sogar nur recht wenige die Animationen bieten. Außerdem gibt es Modeller die Animationen nicht in einer Datei sondern in vielen Einzeldateien speichern. Allerdings heißt das nicht, dass nur die Modelle aus teuren Modellern Animationen (vernünftig) speichern können. Es gibt auch freie und günstige Software (Milkshape 3D), die so etwas kann.&lt;br /&gt;
&lt;br /&gt;
==Welche Modellformate gibt es?==&lt;br /&gt;
Hier eine kleine Auflistung der gebräuchlichsten Formate:&lt;br /&gt;
* .blend ([http://blender3d.org Blender])&lt;br /&gt;
* .collada ([http://www.khronos.org/collada/ Collada])&lt;br /&gt;
* .3ds (3D Studio)&lt;br /&gt;
* .lwo (Lightwave)&lt;br /&gt;
* .ms3d (Milkshape 3D)&lt;br /&gt;
* .bsp (ID Software BSP)&lt;br /&gt;
* .md3 (ID Software MD3)&lt;br /&gt;
* .x (Microsoft DirectX)&lt;br /&gt;
&lt;br /&gt;
==Wozu gibt es Exporter Plugins und Scripte?==&lt;br /&gt;
Für 3D Entwicker ist es wichtig, die erstellten Meshdaten auch in ein vorhandenes oder eigenes Format zu speichern.&lt;br /&gt;
Hierzu gibt es von den meisten 3D Editioren Plugins oder Scripte die dieses erledigen.&lt;br /&gt;
&lt;br /&gt;
Einige Programme wie 3DS Max und Blender bieten beides, wobei in Fall von 3DS Max lieber Plugins verwendet werden sollten (Geschwindigkeitsvorteil). Blender bietet für Scripte einen Python Interpreter, der lediglich ein bischen modifiziert wurde.&lt;br /&gt;
&lt;br /&gt;
Mit der Möglichkeit, eigene Format zu speichern, kann man sich auch aus dem Lizensproblem für bestimmte Formate rauswinden und man hat die Möglichkeit das Format an benötigte Bedingungen anzupassen.&lt;br /&gt;
&lt;br /&gt;
== Format und Model Loader Liste ==&lt;br /&gt;
{|{{Prettytable_B1}}&lt;br /&gt;
!width=&amp;quot;10%&amp;quot;| Format&lt;br /&gt;
!width=&amp;quot;40%&amp;quot;| Format Features&lt;br /&gt;
!width=&amp;quot;50%&amp;quot;| Loader&lt;br /&gt;
|-&lt;br /&gt;
| Cal3D&lt;br /&gt;
|&lt;br /&gt;
* kombinieren von Animationen (30%&amp;amp;nbsp;gehen, 50%&amp;amp;nbsp;rennen, 20%&amp;amp;nbsp;stolzieren und Winken)&lt;br /&gt;
* stufenloses [[Level of Detail]]&lt;br /&gt;
|&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Name&lt;br /&gt;
! nutzbar mit&lt;br /&gt;
! Umgesetzte Features&lt;br /&gt;
|-&lt;br /&gt;
| [http://gna.org/projects/cal3d/ Cal3D]&lt;br /&gt;
| C++&lt;br /&gt;
| alle Cal3D&amp;amp;nbsp;(0.10) Features&lt;br /&gt;
|-&lt;br /&gt;
| DCal3D&lt;br /&gt;
| Delphi / Object Pascal&lt;br /&gt;
| nur Cal3D&amp;amp;nbsp;0.6 Features&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| 3DS&lt;br /&gt;
|&lt;br /&gt;
* Meshbasierendes 3D-Format&lt;br /&gt;
* Vertexbasierende Animationen via Keyframes&lt;br /&gt;
* Normalen werden indirekt über Smoothinggroups gesichert&lt;br /&gt;
'''Format stammt noch aus DOS-zeiten, daher diverse Einschränkungen :'''&lt;br /&gt;
* Maximal 65k Vertices pro [[Mesh]]&lt;br /&gt;
* Texturnamen sind auf [http://de.wikipedia.org/wiki/8.3 8.3 Dateinamen] beschränkt&lt;br /&gt;
* Bones werden nicht exportiert&lt;br /&gt;
* Normalen werden nicht exportiert (nur Smoothinggroups)&lt;br /&gt;
'''Kein freies Format'''&lt;br /&gt;
|&lt;br /&gt;
{|{{Prettytable_B1}} width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Name&lt;br /&gt;
! nutzbar mit&lt;br /&gt;
! Umgesetzte Features&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.delphigl.com/forum/viewtopic.php?t=2421 gl3ds]&lt;br /&gt;
| Delphi / Object Pascal&lt;br /&gt;
| Unbekannt&lt;br /&gt;
|-&lt;br /&gt;
| [http://www.lib3ds.org/ lib3ds]&lt;br /&gt;
| C/C++&lt;br /&gt;
| Unbekannt&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Technik_oder_Algorithmus]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Benutzer:Coolcat&amp;diff=24802</id>
		<title>Benutzer:Coolcat</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Benutzer:Coolcat&amp;diff=24802"/>
				<updated>2010-03-31T15:27:34Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: Semester-Inkrement...gut eigentlich morgen erst, aber ich denke gerade dran ;)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Realname: Martin Weusten&lt;br /&gt;
* Studium:&lt;br /&gt;
** Informatik (Diplom) im 12. Semester&lt;br /&gt;
** Vertiefungsrichtung: Computergrafik&lt;br /&gt;
** RWTH Aachen University&lt;br /&gt;
* Platform: Linux, C++&lt;br /&gt;
* Web: [http://martin-weusten.de martin-weusten.de]&lt;br /&gt;
* Jabber: http://jabber.rwth-aachen.de/w/images/c/ca/Jid-martin.weusten.jpg http://jabber.rwth-aachen.de/w/images/b/ba/Modrewrite-martin.weusten.gif&lt;br /&gt;
::(das ist '''keine''' EMailadresse, sondern eine [http://jabber.rwth-aachen.de/wiki/Jabber_-_Einfach_erkl%C3%A4rt! Jabberadresse!])&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24778</id>
		<title>Deferred Shading</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24778"/>
				<updated>2010-03-15T11:29:21Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Siehe auch */ FBO verlinkt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Deferred Shading =&lt;br /&gt;
&lt;br /&gt;
Der Begriff ''Deferred Shading'' (zu dt. &amp;quot;verzögertes Schattieren&amp;quot;) oder auch ''Deferred Lighting'' (&amp;quot;verzögerte Beleuchtung&amp;quot;) beschreibt eine Technik, mit deren Hilfe die Geometrieverarbeitung von der Lichtberechnung getrennt werden kann. Dies erlaubt hunderte Lichtquellen in einer hoch-komplexen Szene.&lt;br /&gt;
&lt;br /&gt;
== Idee ==&lt;br /&gt;
Um eine Szene mit einem traditionellen Forward-Renderer zu beleuchten, wird normalerweise jedes Objekt der Szene mit den entsprechenden Beleuchtungsparametern der Lichtquelle gezeichnet. Die verschiedenen Ergebnisse werden durch additives Blenden akkumuliert. Für jede Lichtquelle muss also jedes Objekt gezeichnet werden. Angenommen wir haben ''nL'' Lichtquellen und ''nO'' Objekte, dann liegt die Anzahl der Rendercalls bei ''O(nO * nL)''. Natürlich ist es möglich mehrere Lichtquellen in einem Pass (z.B. 8 auf einmal) zu berechnen, aber wir gehen von hunderten Lichtquellen aus, es sind also mehrere Passes notwendig und die Geometrie müsste entsprechend mehrfach verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
''Deferred Shading'' verfolgt einen anderen Ansatz: Anstatt jedes Objekt der Szene für jeden Licht-Pass neu zu renderen, werden zunächst für jeden Pixel alle beleuchtungsrelevanten Daten (z.B. 3D-Position, Normale, Textur-Farbe, etc.) in ein oder mehrere Rendertargets (oftmals auch G-Buffer genannt) geschrieben. Die eigentliche Lichtberechnung kann nun durchgeführt werden indem ein bildschirmfüllendes Quad gerendert wird. Für jeden Pixel werden die Daten aus dem G-Buffer ausgelesen und die Beleuchtung entsprechend dieser Daten berechnet. Die Zahl der Rendercalls liegt bei diesem Ansatz nur noch bei ''O(nO + nL)''. Mit dieser Methode sind hunderte Lichter gleichzeitig in einer hoch-komplexen Szene möglich.&lt;br /&gt;
&lt;br /&gt;
Das Verfahren ist nur dann sinnvoll, wenn man es mit einer sehr komplexen Geometrie und sehr vielen Lichtquellen zu tun hat. Hat man zum Beispiel zwar viele Lichtquellen, aber eine einfache Geometrie, kann bereits ein einfacher Depth-Only-Pass zum initialisieren des Z-Buffers verhindern, dass für unsichtbare Pixel unnötige Lichtberechnungen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Vor-/Nachteile ==&lt;br /&gt;
&lt;br /&gt;
'''Vorteile:'''&lt;br /&gt;
* Viele Lichtquellen bei komplexer Geometrie möglich&lt;br /&gt;
* Postprocessing Effekte (Bloom, Tonemapping, Depth of Field, etc.) können einfach angeschlossen werden, da bereits alle benötigten Informationen aufbereitet vorliegen.&lt;br /&gt;
* Lichtberechnung mit reduzierter Auflösung möglich (sinnvoll z.B. bei FSAA, siehe [[Inferred Lighting]])&lt;br /&gt;
&lt;br /&gt;
'''Nachteile:'''&lt;br /&gt;
* Kein Hardware Anti-Aliasing möglich&lt;br /&gt;
* Transparente Objekte müssen getrennt behandelt werden (Abhilfe schafft [[Inferred Lighting]])&lt;br /&gt;
* Nur auf neueren Grafikkarten mit MRT (Mutliple Render Target) und Floating Point-Textur Support möglich (ab DirectX 9 kompatiblem Grafikchip; OpenGL Version ???)&lt;br /&gt;
&lt;br /&gt;
== Implementierung ==&lt;br /&gt;
Zunächst müssen die Geometriedaten in den (G-Buffer) geschrieben werden. Hierzu benötigen wir drei 16-Bit Float Rendertarget Texturen. Niedrigere Bittiefen sind für Positionsbeschreibung und Normalenvektor zu wenig. Alle Rendertarget-Texturen müssen die gleiche Bittiefe haben. Sind mehrere Rendertargets aktiviert, so wird Hardware-Anti-Aliasing automatisch abgeschaltet. Die folgenden Beispiele sind mit Andorra 2D entwickelt und sollten recht einfach verständlich und nach OpenGL/DirectX umzusetzen sein.&lt;br /&gt;
&lt;br /&gt;
'''Schritt 1: Erstellen der Rendertargets'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
FRT_1_Albedo := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
FRT_1_Albedo.BitDepth := adF64Bit;&lt;br /&gt;
FRT_1_Albedo.SetSize(surfacew, surfaceh);&lt;br /&gt;
FRT_1_Albedo.Filter := atPoint;&lt;br /&gt;
&lt;br /&gt;
FRT_2_Position := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_3_Normal := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_4_Composite := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Schritt 2: Der G-Buffer-Fill Shader (HLSL/Cg)'''&amp;lt;br&amp;gt;&lt;br /&gt;
Der hier gezeigte Shader ist nur ein einfaches Beispiel. Unter anderem kann durch das geschickte Weglassen von Daten (Berechnen von X, Y aus den Z und den Screen-Coordinaten im Lighting-Shader) Bandbreite eingespart werden.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//Fragment Shader&lt;br /&gt;
&lt;br /&gt;
struct fs_res {&lt;br /&gt;
  float4 position: COLOR1;&lt;br /&gt;
  float4 normal: COLOR2;&lt;br /&gt;
  float4 albedo: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
fs_res fs_std_geometry(&lt;br /&gt;
  float3 viewpos: TEXCOORD0,&lt;br /&gt;
  float3 normal: TEXCOORD1&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  fs_res OUT;  &lt;br /&gt;
   &lt;br /&gt;
  OUT.position = float4(viewpos, 1.0f); &lt;br /&gt;
  OUT.normal = float4(normal, 1.0f);&lt;br /&gt;
  OUT.albedo = float4(1.0f, 1.0f, 1.0f , 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Vertex Shader&lt;br /&gt;
&lt;br /&gt;
struct vs_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 viewpos : TEXCOORD0;  &lt;br /&gt;
  float3 normal: TEXCOORD1;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_res vs_std_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  float3 normal: NORMAL,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_res OUT; &lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = mul(float4(position, 1.0f), modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(float4(position, 1.0f), modelview);&lt;br /&gt;
  OUT.normal = normalize(mul(float4(normal, 0.0f), modelview));&lt;br /&gt;
&lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 3: Daten in G-Buffer Rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Activate the G-Buffers&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_1_Albedo.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, FRT_2_Position.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(0, 0, 0, 0), 1, 1);&lt;br /&gt;
&lt;br /&gt;
  //Activate the shaders&lt;br /&gt;
  FTransformShader.FragmentShader.BindEffect;&lt;br /&gt;
  FTransformShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  FScene.Draw(FCamera, nil, 0);&lt;br /&gt;
&lt;br /&gt;
  //Deactivate the shaders&lt;br /&gt;
  FTransformShader.VertexShader.UnbindEffect;&lt;br /&gt;
  FTransformShader.FragmentShader.UnbindEffect;&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, nil);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 4: Einfacher Direktionaler Licht-Shader'''&amp;lt;br&amp;gt;&lt;br /&gt;
Das zur Beleuchtung verwendete Quad liegt in diesem (einfachen) Fall bereits projeziert in normierten Koordinaten vor.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct ps_light_res {&lt;br /&gt;
  float4 color: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
ps_light_res ps_light_geometry(&lt;br /&gt;
  float3 screenpos: TEXCOORD0,&lt;br /&gt;
  float3 viewpos: TEXCOORD1,&lt;br /&gt;
  float3 lightdir: TEXCOORD2,&lt;br /&gt;
  &lt;br /&gt;
  uniform sampler sPosition,&lt;br /&gt;
  uniform sampler sNormal,&lt;br /&gt;
  uniform sampler sAlbedo,&lt;br /&gt;
  uniform float4 lightcolor&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  ps_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  float4 col = tex2D(sAlbedo, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  float4 normal = tex2D(sNormal, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  &lt;br /&gt;
  OUT.color = float4((lightcolor * col * clamp(dot(normal.xyz, lightdir), 0.0f, 1.0f)).rgb, 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct vs_light_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 screenpos: TEXCOORD0;&lt;br /&gt;
  float3 viewpos: TEXCOORD1;&lt;br /&gt;
  float3 lightdir: TEXCOORD2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_light_res vs_light_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj,&lt;br /&gt;
  uniform float3 lightdir&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = float4(position, 1.0f);&lt;br /&gt;
  OUT.screenpos = position * float3(0.5f, 0.5f, 0.0f) + float3(0.5f, 0.5f, 0.0f);//mul(pos, modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(position, modelview);&lt;br /&gt;
  OUT.lightdir = normalize(mul(float4(lightdir, 0.0f), modelview));&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 5: Lichtquellen in das Composite RT rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Render a directional light using a quad&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_4_Composite.Texture);&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(255, 0, 0, 0), 1, 1);  &lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sAlbedo', FRT_1_Albedo.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sPosition', FRT_2_Position.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sNormal', FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.BindEffect;&lt;br /&gt;
  FLightShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  //Render a blue light&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('lightcolor', Ad_ARGB(255, 50, 50, 100));&lt;br /&gt;
  FLightShader.VertexShader.SetParameter('lightdir', AcVector_Normalize(AcVector3(2.0, 2.0, -1.0)));&lt;br /&gt;
  FQuad.BlendMode = bmAdd;&lt;br /&gt;
  FQuad.Draw(nil);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.UnbindEffect;&lt;br /&gt;
  FLightShader.VertexShader.UnbindEffect;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 6: Das Ergebnis Zeichnen'''&amp;lt;br&amp;gt;&lt;br /&gt;
Nun muss nur noch das Ergebnis im &amp;quot;Composite&amp;quot;-RT gezeichnet werden - oder weitere Post-Processing Maßnahmen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
* http://www.gamedev.net/reference/programming/features/defRender/&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Shader]]&lt;br /&gt;
* [[FBO]]&lt;br /&gt;
* [[Inferred Lighting]]&lt;br /&gt;
* [[Hintergrundwissen#Render-Pipeline|Render-Pipeline]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Hintergrundwissen&amp;diff=24777</id>
		<title>Hintergrundwissen</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Hintergrundwissen&amp;diff=24777"/>
				<updated>2010-03-15T11:18:05Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Optimierungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Was hier hinein gehört ==&lt;br /&gt;
&lt;br /&gt;
In diese Kategorie kommt alles rein was zum Verständnis im Bereich OpenGL/3D-Programmierung nützlich sein könnte. Also u.a. welche Puffer es unter OpenGL gibt, und für was die gut sind.&lt;br /&gt;
&lt;br /&gt;
Auch hier sollte man möglichst aufspalten und dann eine Überschrift setzen. So macht man das Auffinden bestimmter Dinge leichter und auch die Verlinkung darauf ist oft etwas das man in anderen Dokumenten benötigt.&lt;br /&gt;
&lt;br /&gt;
P.S. : Ob hier Deutsche Begriffe oder Anglizismen genutzt werden ist jedem selbst überlassen. Ich persönlich bevorzuge ersteres.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Begriffserklärungen ==&lt;br /&gt;
In diesem Teil findet ihr Erklärungen zu begriffen, die ihr ständig im Zusammenhang mit OpenGL höhrt. Wenn ihr etwas nicht kennt, könnt ihr hier nachsehen, ob der Begriff bereits erklärt wurde.&lt;br /&gt;
&lt;br /&gt;
=== Grundlagen ===&lt;br /&gt;
: [[Alpha-Clipping|&amp;amp;alpha;-Clipping]] &lt;br /&gt;
: [[Funktions Anhang|Aufbau von OpenGL Funktionen]] &lt;br /&gt;
: [[Bounding Volume]] ([[Bounding Box]])&lt;br /&gt;
: [[Bresenham-Algorithmus|Bresenham Algorithmus (Linien / Kurven zeichnen)]]&lt;br /&gt;
: [[Clipping Plane]] {{excIcon}} (Schnittebenen)&lt;br /&gt;
: [[Frustum]]&lt;br /&gt;
: [[OpenGL Name|Namen in OpenGL]]&lt;br /&gt;
: [[Projektions-Transformationen]]&lt;br /&gt;
: [[Scanline-Rasterization|Scanline Algorithmus (Polygone zeichen)]]&lt;br /&gt;
: [[Normalen]]&lt;br /&gt;
: [[Primitive]]&lt;br /&gt;
: [[Texel]] {{icpIcon}}&lt;br /&gt;
&lt;br /&gt;
=== Mathematische Grundlagen ===&lt;br /&gt;
: [[Matrix|Matrix / Matrizen]]&lt;br /&gt;
: [[Quaternion|Quaternion / Hamiltonsche Quaternionen]]&lt;br /&gt;
: [[Standard Skalarprodukt|Skalarprodukt / Punktprodukt / Dotproduct / Dot]]&lt;br /&gt;
: [[Vektorprodukt|Vektorprodukt / Vektorkreuzprodukt / Kreuzprodukt]]&lt;br /&gt;
&lt;br /&gt;
=== Bildentstehung ===&lt;br /&gt;
: [[Dithering]] {{icpIcon}}&lt;br /&gt;
: [[Fragment| Fragmente]]&lt;br /&gt;
: [[Pixel]]&lt;br /&gt;
: [[Rasterisierung]]&lt;br /&gt;
: [[Tiefentest]]&lt;br /&gt;
&lt;br /&gt;
=== Puffer ===&lt;br /&gt;
: [[Akkumulationspuffer]] (Accumulationbuffer)&lt;br /&gt;
: [[Doppelpufferung]] (Doublebuffering)&lt;br /&gt;
: [[Farbpuffer]] (Colorbuffer)&lt;br /&gt;
: [[Feedbackpuffer]]&lt;br /&gt;
: [[Framebuffer]]&lt;br /&gt;
: [[Logischer Puffer]]&lt;br /&gt;
: [[Schablonenpuffer]] (Stencilbuffer)&lt;br /&gt;
: [[Selektionspuffer]]&lt;br /&gt;
: [[Tiefenpuffer]] (Depthbuffer)&lt;br /&gt;
&lt;br /&gt;
=== Texturen ===&lt;br /&gt;
''Wer Informationen zum Erstellen von Texturen benötigt, sollte sich die [[Link]]-Sektion und den Artikel zum Thema [[Farbraum|Farbräume]]{{excIcon}} ansehen.''&lt;br /&gt;
: [[Anisotropes Filtern]]&lt;br /&gt;
: [[Decals]]&lt;br /&gt;
: [[MipMaps]]&lt;br /&gt;
: [[Register Combiner]]&lt;br /&gt;
: [[Texel Berechnung]]&lt;br /&gt;
: [[Texturen]]&lt;br /&gt;
: [[Texturesynthesis]] ''automatisiertes Erzeugen von (u.A. kachelbaren) Texturen ohne Wiederholung''&lt;br /&gt;
&lt;br /&gt;
=== Textureneffekte ===&lt;br /&gt;
: [[Animierte Textur]]en&lt;br /&gt;
: [[Bumpmapping]] (Simulation rauher Oberflächen)&lt;br /&gt;
: [[Displacementmapping]] (Änderung der Oberflächengeometrie)&lt;br /&gt;
: [[Detailmapping]] (Erhöhung des Texturdetailgrades)&lt;br /&gt;
: [[Environment Mapping]] (Umgebungsspiegelung)&lt;br /&gt;
:: [[Cubemap]]ping {{icpIcon}}&lt;br /&gt;
:: [[Spheremap]]ping {{icpIcon}}&lt;br /&gt;
: [[Multitexturing]]&lt;br /&gt;
: [[Texture Stage]]&lt;br /&gt;
&lt;br /&gt;
=== Effekte ===&lt;br /&gt;
: [[Antialiasing]] (Kantenglättung)&lt;br /&gt;
: [[Blenden]] (Blending)&lt;br /&gt;
: [[Jittering]] (Verwischen)&lt;br /&gt;
: [[Nebel]] (Fogging)&lt;br /&gt;
: [[Reflexion]] (Spiegelungen)&lt;br /&gt;
: [[Transparenz]] (durchsichtige Objekte)&lt;br /&gt;
&lt;br /&gt;
=== Optimierungen ===&lt;br /&gt;
: [[Backfaceculling]]&lt;br /&gt;
: [[Displaylisten]]&lt;br /&gt;
: [[Frustum Culling]] &lt;br /&gt;
: [[Triangulation]]&lt;br /&gt;
: [[VBO]] (Vertex Buffer Objects)&lt;br /&gt;
: [[Occlusion Query|Occlusion Queries]]&lt;br /&gt;
: [[Performance]] (Allgemeine Performance-Tipps)&lt;br /&gt;
: [[Instancing]]&lt;br /&gt;
: [[Deferred Shading]]&lt;br /&gt;
: [[Inferred Lighting]]&lt;br /&gt;
&lt;br /&gt;
=== Weiterführendes ===&lt;br /&gt;
: [[Beleuchtung]]&lt;br /&gt;
: [[Billboard]]s &lt;br /&gt;
:: [[sphärisches Billboard]] {{icpIcon}}&lt;br /&gt;
:: [[zylindrisches Billboard]] {{icpIcon}}&lt;br /&gt;
: [[Convolution]]&lt;br /&gt;
: [[NURBS]]&lt;br /&gt;
: [[Picking]]&lt;br /&gt;
: [[Point Sprite]]&lt;br /&gt;
: [[Quadrik]]en&lt;br /&gt;
: [[Selektion]]&lt;br /&gt;
: [[Sprite]]s&lt;br /&gt;
: [[Tesselierung]]&lt;br /&gt;
&lt;br /&gt;
=== Fehler/ Nebeneffekte ===&lt;br /&gt;
: [[Aliasing]]  (Treppenbildung und Ähnliches)&lt;br /&gt;
: [[Popping]]   (Plötzliches Auftauchen von Elementen. Z.B. bekannt bei [[LOD]])&lt;br /&gt;
: [[ZFighting]] (Flimmernde Polygone und Kanten)&lt;br /&gt;
&lt;br /&gt;
=== Render-Pipeline ===&lt;br /&gt;
: [[Feste Funktionspipeline]] {{icpIcon}}&lt;br /&gt;
: [[GLSlang]] &lt;br /&gt;
: [[GLSL]]&lt;br /&gt;
: [[Cg]]&lt;br /&gt;
: [[Shader]] (allgemein)&lt;br /&gt;
:: [[Vertexshader]]&lt;br /&gt;
:: [[Geometryshader]]&lt;br /&gt;
:: [[Fragmentshader]] (auch ''Pixelshader'')&lt;br /&gt;
: [[Transform-Feedback‎]] (auch ''Stream-Out'')&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken ===&lt;br /&gt;
: [[DGLSDK]]&lt;br /&gt;
: [[OpenAL]]&lt;br /&gt;
: [[SDL]]&lt;br /&gt;
&lt;br /&gt;
=== Grafikkarten ===&lt;br /&gt;
: [[Bandbreitenlimitiert]]&lt;br /&gt;
: [[Shaderlimitiert]]&lt;br /&gt;
: [[Vertex Cache]]&lt;br /&gt;
: [[Vertexlimitiert]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Sonstiges ===&lt;br /&gt;
: [[Benchmark]]&lt;br /&gt;
: [[Callback]]&lt;br /&gt;
: [[Farbraum]]&lt;br /&gt;
: [[Füllrate]]&lt;br /&gt;
: [[hexfeld | Hexfeld rendern]]&lt;br /&gt;
: [[Raytracing]]&lt;br /&gt;
: [[Renderkontext]]&lt;br /&gt;
: [[Scanlinerenderer]]&lt;br /&gt;
: [[Timebased Movement]]&lt;br /&gt;
&lt;br /&gt;
=== Bücher zum Thema ... ===&lt;br /&gt;
: ... OpenGL: [[RedBook]]&lt;br /&gt;
: ... OpenGL: [[BlueBook]]&lt;br /&gt;
: ... OpenGL: [[PurpleBook]]&lt;br /&gt;
: ... OpenGL: [[OrangeBook]]&lt;br /&gt;
: ... OpenGL: [http://www.starstonesoftware.com/OpenGL/ OpenGL Superbible]&lt;br /&gt;
&lt;br /&gt;
== Checklisten ==&lt;br /&gt;
In diesem Teil findet ihr Checklisten die ihr durcharbeiten solltet, wenn ihr Fehler im Code habt, bzw. wenn eure Ausgabe nicht so aussieht wie sie es söllte.&lt;br /&gt;
&lt;br /&gt;
:[[Checkliste Blending]]&lt;br /&gt;
:[[Checkliste Positionierung]]&lt;br /&gt;
:[[Checkliste Selektion]]&lt;br /&gt;
:[[Checkliste Shader]]&lt;br /&gt;
:[[Checkliste Texturierung]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Inferred_Lighting&amp;diff=24776</id>
		<title>Inferred Lighting</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Inferred_Lighting&amp;diff=24776"/>
				<updated>2010-03-15T11:15:29Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: nur ein stub&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Unvollständig|Inferred Lighting ist nicht so unglaublich kompliziert, vielleicht hat&amp;lt;br&amp;gt;ja jemand Lust&amp;amp;Zeit das verlinkte Paper ins Wiki zu übertragen.}}&lt;br /&gt;
&lt;br /&gt;
''Inferred Lighting'' ist eine Erweiterung des [[Deferred Shading]]. Sie erlaubt zum einen auch eine begrenzte Anzahl transparenter Objekte. Zum anderen ist auch eine Lichtberechnung mit reduzierter Auflösung möglich, was bei extrem hohen Bildschirmauflösungen (z.B. bei FSAA) sinnvoll wird.&lt;br /&gt;
&lt;br /&gt;
== Literatur ==&lt;br /&gt;
* ''S. Kircher and A. Lawrance.''&amp;lt;br&amp;gt;Inferred Lighting: Fast dynamic lighting and shadows for opaque and transluscent objects.&amp;lt;br&amp;gt;SIGGRAPH 2009: Game Paper Proceedings, August 2009. ([http://graphics.cs.uiuc.edu/~kircher/inferred/inferred_lighting_paper.pdf PDF], [http://doi.acm.org/10.1145/1581073.1581080 ACM-DOI])&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Shader]]&lt;br /&gt;
* [[Deferred Shading]]&lt;br /&gt;
* [[Hintergrundwissen#Render-Pipeline|Render-Pipeline]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24775</id>
		<title>Deferred Shading</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24775"/>
				<updated>2010-03-15T11:07:12Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Vor-/Nachteile */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Deferred Shading =&lt;br /&gt;
&lt;br /&gt;
Der Begriff ''Deferred Shading'' (zu dt. &amp;quot;verzögertes Schattieren&amp;quot;) oder auch ''Deferred Lighting'' (&amp;quot;verzögerte Beleuchtung&amp;quot;) beschreibt eine Technik, mit deren Hilfe die Geometrieverarbeitung von der Lichtberechnung getrennt werden kann. Dies erlaubt hunderte Lichtquellen in einer hoch-komplexen Szene.&lt;br /&gt;
&lt;br /&gt;
== Idee ==&lt;br /&gt;
Um eine Szene mit einem traditionellen Forward-Renderer zu beleuchten, wird normalerweise jedes Objekt der Szene mit den entsprechenden Beleuchtungsparametern der Lichtquelle gezeichnet. Die verschiedenen Ergebnisse werden durch additives Blenden akkumuliert. Für jede Lichtquelle muss also jedes Objekt gezeichnet werden. Angenommen wir haben ''nL'' Lichtquellen und ''nO'' Objekte, dann liegt die Anzahl der Rendercalls bei ''O(nO * nL)''. Natürlich ist es möglich mehrere Lichtquellen in einem Pass (z.B. 8 auf einmal) zu berechnen, aber wir gehen von hunderten Lichtquellen aus, es sind also mehrere Passes notwendig und die Geometrie müsste entsprechend mehrfach verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
''Deferred Shading'' verfolgt einen anderen Ansatz: Anstatt jedes Objekt der Szene für jeden Licht-Pass neu zu renderen, werden zunächst für jeden Pixel alle beleuchtungsrelevanten Daten (z.B. 3D-Position, Normale, Textur-Farbe, etc.) in ein oder mehrere Rendertargets (oftmals auch G-Buffer genannt) geschrieben. Die eigentliche Lichtberechnung kann nun durchgeführt werden indem ein bildschirmfüllendes Quad gerendert wird. Für jeden Pixel werden die Daten aus dem G-Buffer ausgelesen und die Beleuchtung entsprechend dieser Daten berechnet. Die Zahl der Rendercalls liegt bei diesem Ansatz nur noch bei ''O(nO + nL)''. Mit dieser Methode sind hunderte Lichter gleichzeitig in einer hoch-komplexen Szene möglich.&lt;br /&gt;
&lt;br /&gt;
Das Verfahren ist nur dann sinnvoll, wenn man es mit einer sehr komplexen Geometrie und sehr vielen Lichtquellen zu tun hat. Hat man zum Beispiel zwar viele Lichtquellen, aber eine einfache Geometrie, kann bereits ein einfacher Depth-Only-Pass zum initialisieren des Z-Buffers verhindern, dass für unsichtbare Pixel unnötige Lichtberechnungen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Vor-/Nachteile ==&lt;br /&gt;
&lt;br /&gt;
'''Vorteile:'''&lt;br /&gt;
* Viele Lichtquellen bei komplexer Geometrie möglich&lt;br /&gt;
* Postprocessing Effekte (Bloom, Tonemapping, Depth of Field, etc.) können einfach angeschlossen werden, da bereits alle benötigten Informationen aufbereitet vorliegen.&lt;br /&gt;
* Lichtberechnung mit reduzierter Auflösung möglich (sinnvoll z.B. bei FSAA, siehe [[Inferred Lighting]])&lt;br /&gt;
&lt;br /&gt;
'''Nachteile:'''&lt;br /&gt;
* Kein Hardware Anti-Aliasing möglich&lt;br /&gt;
* Transparente Objekte müssen getrennt behandelt werden (Abhilfe schafft [[Inferred Lighting]])&lt;br /&gt;
* Nur auf neueren Grafikkarten mit MRT (Mutliple Render Target) und Floating Point-Textur Support möglich (ab DirectX 9 kompatiblem Grafikchip; OpenGL Version ???)&lt;br /&gt;
&lt;br /&gt;
== Implementierung ==&lt;br /&gt;
Zunächst müssen die Geometriedaten in den (G-Buffer) geschrieben werden. Hierzu benötigen wir drei 16-Bit Float Rendertarget Texturen. Niedrigere Bittiefen sind für Positionsbeschreibung und Normalenvektor zu wenig. Alle Rendertarget-Texturen müssen die gleiche Bittiefe haben. Sind mehrere Rendertargets aktiviert, so wird Hardware-Anti-Aliasing automatisch abgeschaltet. Die folgenden Beispiele sind mit Andorra 2D entwickelt und sollten recht einfach verständlich und nach OpenGL/DirectX umzusetzen sein.&lt;br /&gt;
&lt;br /&gt;
'''Schritt 1: Erstellen der Rendertargets'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
FRT_1_Albedo := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
FRT_1_Albedo.BitDepth := adF64Bit;&lt;br /&gt;
FRT_1_Albedo.SetSize(surfacew, surfaceh);&lt;br /&gt;
FRT_1_Albedo.Filter := atPoint;&lt;br /&gt;
&lt;br /&gt;
FRT_2_Position := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_3_Normal := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_4_Composite := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Schritt 2: Der G-Buffer-Fill Shader (HLSL/Cg)'''&amp;lt;br&amp;gt;&lt;br /&gt;
Der hier gezeigte Shader ist nur ein einfaches Beispiel. Unter anderem kann durch das geschickte Weglassen von Daten (Berechnen von X, Y aus den Z und den Screen-Coordinaten im Lighting-Shader) Bandbreite eingespart werden.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//Fragment Shader&lt;br /&gt;
&lt;br /&gt;
struct fs_res {&lt;br /&gt;
  float4 position: COLOR1;&lt;br /&gt;
  float4 normal: COLOR2;&lt;br /&gt;
  float4 albedo: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
fs_res fs_std_geometry(&lt;br /&gt;
  float3 viewpos: TEXCOORD0,&lt;br /&gt;
  float3 normal: TEXCOORD1&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  fs_res OUT;  &lt;br /&gt;
   &lt;br /&gt;
  OUT.position = float4(viewpos, 1.0f); &lt;br /&gt;
  OUT.normal = float4(normal, 1.0f);&lt;br /&gt;
  OUT.albedo = float4(1.0f, 1.0f, 1.0f , 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Vertex Shader&lt;br /&gt;
&lt;br /&gt;
struct vs_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 viewpos : TEXCOORD0;  &lt;br /&gt;
  float3 normal: TEXCOORD1;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_res vs_std_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  float3 normal: NORMAL,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_res OUT; &lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = mul(float4(position, 1.0f), modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(float4(position, 1.0f), modelview);&lt;br /&gt;
  OUT.normal = normalize(mul(float4(normal, 0.0f), modelview));&lt;br /&gt;
&lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 3: Daten in G-Buffer Rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Activate the G-Buffers&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_1_Albedo.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, FRT_2_Position.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(0, 0, 0, 0), 1, 1);&lt;br /&gt;
&lt;br /&gt;
  //Activate the shaders&lt;br /&gt;
  FTransformShader.FragmentShader.BindEffect;&lt;br /&gt;
  FTransformShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  FScene.Draw(FCamera, nil, 0);&lt;br /&gt;
&lt;br /&gt;
  //Deactivate the shaders&lt;br /&gt;
  FTransformShader.VertexShader.UnbindEffect;&lt;br /&gt;
  FTransformShader.FragmentShader.UnbindEffect;&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, nil);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 4: Einfacher Direktionaler Licht-Shader'''&amp;lt;br&amp;gt;&lt;br /&gt;
Das zur Beleuchtung verwendete Quad liegt in diesem (einfachen) Fall bereits projeziert in normierten Koordinaten vor.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct ps_light_res {&lt;br /&gt;
  float4 color: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
ps_light_res ps_light_geometry(&lt;br /&gt;
  float3 screenpos: TEXCOORD0,&lt;br /&gt;
  float3 viewpos: TEXCOORD1,&lt;br /&gt;
  float3 lightdir: TEXCOORD2,&lt;br /&gt;
  &lt;br /&gt;
  uniform sampler sPosition,&lt;br /&gt;
  uniform sampler sNormal,&lt;br /&gt;
  uniform sampler sAlbedo,&lt;br /&gt;
  uniform float4 lightcolor&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  ps_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  float4 col = tex2D(sAlbedo, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  float4 normal = tex2D(sNormal, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  &lt;br /&gt;
  OUT.color = float4((lightcolor * col * clamp(dot(normal.xyz, lightdir), 0.0f, 1.0f)).rgb, 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct vs_light_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 screenpos: TEXCOORD0;&lt;br /&gt;
  float3 viewpos: TEXCOORD1;&lt;br /&gt;
  float3 lightdir: TEXCOORD2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_light_res vs_light_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj,&lt;br /&gt;
  uniform float3 lightdir&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = float4(position, 1.0f);&lt;br /&gt;
  OUT.screenpos = position * float3(0.5f, 0.5f, 0.0f) + float3(0.5f, 0.5f, 0.0f);//mul(pos, modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(position, modelview);&lt;br /&gt;
  OUT.lightdir = normalize(mul(float4(lightdir, 0.0f), modelview));&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 5: Lichtquellen in das Composite RT rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Render a directional light using a quad&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_4_Composite.Texture);&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(255, 0, 0, 0), 1, 1);  &lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sAlbedo', FRT_1_Albedo.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sPosition', FRT_2_Position.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sNormal', FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.BindEffect;&lt;br /&gt;
  FLightShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  //Render a blue light&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('lightcolor', Ad_ARGB(255, 50, 50, 100));&lt;br /&gt;
  FLightShader.VertexShader.SetParameter('lightdir', AcVector_Normalize(AcVector3(2.0, 2.0, -1.0)));&lt;br /&gt;
  FQuad.BlendMode = bmAdd;&lt;br /&gt;
  FQuad.Draw(nil);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.UnbindEffect;&lt;br /&gt;
  FLightShader.VertexShader.UnbindEffect;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 6: Das Ergebnis Zeichnen'''&amp;lt;br&amp;gt;&lt;br /&gt;
Nun muss nur noch das Ergebnis im &amp;quot;Composite&amp;quot;-RT gezeichnet werden - oder weitere Post-Processing Maßnahmen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
* http://www.gamedev.net/reference/programming/features/defRender/&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Shader]]&lt;br /&gt;
* [[Inferred Lighting]]&lt;br /&gt;
* [[Hintergrundwissen#Render-Pipeline|Render-Pipeline]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24773</id>
		<title>Deferred Shading</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24773"/>
				<updated>2010-03-15T11:00:23Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Siehe auch */ Inferred Lighting&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Deferred Shading =&lt;br /&gt;
&lt;br /&gt;
Der Befriff ''Deferred Shading'' (zu dt. &amp;quot;verzögertes Schattieren&amp;quot;) oder auch ''Deferred Lighting'' (&amp;quot;verzögerte Beleuchtung&amp;quot;) beschreibt eine Technik, mit deren Hilfe die Geometrieverarbeitung von der Lichtberechnung getrennt werden kann. Dies erlaubt hunderte Lichtquellen in einer hoch-komplexen Szene.&lt;br /&gt;
&lt;br /&gt;
== Idee ==&lt;br /&gt;
Um eine Szene mit einem traditionellen Forward-Renderer zu beleuchten, wird normalerweise jedes Objekt der Szene mit den entsprechenden Beleuchtungsparametern der Lichtquelle gezeichnet. Die verschiedenen Ergebnisse werden durch additives Blenden akkumuliert. Für jede Lichtquelle muss also jedes Objekt gezeichnet werden. Angenommen wir haben ''nL'' Lichtquellen und ''nO'' Objekte, dann liegt die Anzahl der Rendercalls bei ''O(nO * nL)''. Natürlich ist es möglich mehrere Lichtquellen in einem Pass (z.B. 8 auf einmal) zu berechnen, aber wir gehen von hunderten Lichtquellen aus, es sind also mehrere Passes notwendig und die Geometrie müsste entsprechend mehrfach verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
''Deferred Shading'' verfolgt einen anderen Ansatz: Anstatt jedes Objekt der Szene für jeden Licht-Pass neu zu renderen, werden zunächst für jeden Pixel alle beleuchtungsrelevanten Daten (z.B. 3D-Position, Normale, Textur-Farbe, etc.) in ein oder mehrere Rendertargets (oftmals auch G-Buffer genannt) geschrieben. Die eigentliche Lichtberechnung kann nun durchgeführt werden indem ein bildschirmfüllendes Quad gerendert wird. Für jeden Pixel werden die Daten aus dem G-Buffer ausgelesen und die Beleuchtung entsprechend dieser Daten berechnet. Die Zahl der Rendercalls liegt bei diesem Ansatz nur noch bei ''O(nO + nL)''. Mit dieser Methode sind hunderte Lichter gleichzeitig in einer hoch-komplexen Szene möglich.&lt;br /&gt;
&lt;br /&gt;
Das Verfahren ist nur dann sinnvoll, wenn man es mit einer sehr komplexen Geometrie und sehr vielen Lichtquellen zu tun hat. Hat man zum Beispiel zwar viele Lichtquellen, aber eine einfache Geometrie, kann bereits ein einfacher Depth-Only-Pass zum initialisieren des Z-Buffers verhindern, dass für unsichtbare Pixel unnötige Lichtberechnungen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Vor-/Nachteile ==&lt;br /&gt;
&lt;br /&gt;
'''Vorteile:'''&lt;br /&gt;
* Viele Lichtquellen bei komplexer Geometrie möglich&lt;br /&gt;
* Postprocessing Effekte (Bloom, Tonemapping, Depth of Field, etc.) können einfach angeschlossen werden, da bereits alle benötigten Informationen aufbereitet vorliegen.&lt;br /&gt;
* Lichtberechnung mit reduzierter Auflösung möglich (sinnvoll z.B. bei FSAA)&lt;br /&gt;
&lt;br /&gt;
'''Nachteile:'''&lt;br /&gt;
* Kein Hardware Anti-Aliasing möglich&lt;br /&gt;
* Transparente Objekte müssen getrennt behandelt werden (Abhilfe schafft [[Inferred Lighting]])&lt;br /&gt;
* Nur auf neueren Grafikkarten mit MRT (Mutliple Render Target) und Floating Point-Textur Support möglich (ab DirectX 9 kompatiblem Grafikchip; OpenGL Version ???)&lt;br /&gt;
&lt;br /&gt;
== Implementierung ==&lt;br /&gt;
Zunächst müssen die Geometriedaten in den (G-Buffer) geschrieben werden. Hierzu benötigen wir drei 16-Bit Float Rendertarget Texturen. Niedrigere Bittiefen sind für Positionsbeschreibung und Normalenvektor zu wenig. Alle Rendertarget-Texturen müssen die gleiche Bittiefe haben. Sind mehrere Rendertargets aktiviert, so wird Hardware-Anti-Aliasing automatisch abgeschaltet. Die folgenden Beispiele sind mit Andorra 2D entwickelt und sollten recht einfach verständlich und nach OpenGL/DirectX umzusetzen sein.&lt;br /&gt;
&lt;br /&gt;
'''Schritt 1: Erstellen der Rendertargets'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
FRT_1_Albedo := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
FRT_1_Albedo.BitDepth := adF64Bit;&lt;br /&gt;
FRT_1_Albedo.SetSize(surfacew, surfaceh);&lt;br /&gt;
FRT_1_Albedo.Filter := atPoint;&lt;br /&gt;
&lt;br /&gt;
FRT_2_Position := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_3_Normal := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_4_Composite := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Schritt 2: Der G-Buffer-Fill Shader (HLSL/Cg)'''&amp;lt;br&amp;gt;&lt;br /&gt;
Der hier gezeigte Shader ist nur ein einfaches Beispiel. Unter anderem kann durch das geschickte Weglassen von Daten (Berechnen von X, Y aus den Z und den Screen-Coordinaten im Lighting-Shader) Bandbreite eingespart werden.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//Fragment Shader&lt;br /&gt;
&lt;br /&gt;
struct fs_res {&lt;br /&gt;
  float4 position: COLOR1;&lt;br /&gt;
  float4 normal: COLOR2;&lt;br /&gt;
  float4 albedo: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
fs_res fs_std_geometry(&lt;br /&gt;
  float3 viewpos: TEXCOORD0,&lt;br /&gt;
  float3 normal: TEXCOORD1&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  fs_res OUT;  &lt;br /&gt;
   &lt;br /&gt;
  OUT.position = float4(viewpos, 1.0f); &lt;br /&gt;
  OUT.normal = float4(normal, 1.0f);&lt;br /&gt;
  OUT.albedo = float4(1.0f, 1.0f, 1.0f , 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Vertex Shader&lt;br /&gt;
&lt;br /&gt;
struct vs_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 viewpos : TEXCOORD0;  &lt;br /&gt;
  float3 normal: TEXCOORD1;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_res vs_std_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  float3 normal: NORMAL,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_res OUT; &lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = mul(float4(position, 1.0f), modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(float4(position, 1.0f), modelview);&lt;br /&gt;
  OUT.normal = normalize(mul(float4(normal, 0.0f), modelview));&lt;br /&gt;
&lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 3: Daten in G-Buffer Rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Activate the G-Buffers&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_1_Albedo.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, FRT_2_Position.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(0, 0, 0, 0), 1, 1);&lt;br /&gt;
&lt;br /&gt;
  //Activate the shaders&lt;br /&gt;
  FTransformShader.FragmentShader.BindEffect;&lt;br /&gt;
  FTransformShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  FScene.Draw(FCamera, nil, 0);&lt;br /&gt;
&lt;br /&gt;
  //Deactivate the shaders&lt;br /&gt;
  FTransformShader.VertexShader.UnbindEffect;&lt;br /&gt;
  FTransformShader.FragmentShader.UnbindEffect;&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, nil);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 4: Einfacher Direktionaler Licht-Shader'''&amp;lt;br&amp;gt;&lt;br /&gt;
Das zur Beleuchtung verwendete Quad liegt in diesem (einfachen) Fall bereits projeziert in normierten Koordinaten vor.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct ps_light_res {&lt;br /&gt;
  float4 color: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
ps_light_res ps_light_geometry(&lt;br /&gt;
  float3 screenpos: TEXCOORD0,&lt;br /&gt;
  float3 viewpos: TEXCOORD1,&lt;br /&gt;
  float3 lightdir: TEXCOORD2,&lt;br /&gt;
  &lt;br /&gt;
  uniform sampler sPosition,&lt;br /&gt;
  uniform sampler sNormal,&lt;br /&gt;
  uniform sampler sAlbedo,&lt;br /&gt;
  uniform float4 lightcolor&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  ps_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  float4 col = tex2D(sAlbedo, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  float4 normal = tex2D(sNormal, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  &lt;br /&gt;
  OUT.color = float4((lightcolor * col * clamp(dot(normal.xyz, lightdir), 0.0f, 1.0f)).rgb, 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct vs_light_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 screenpos: TEXCOORD0;&lt;br /&gt;
  float3 viewpos: TEXCOORD1;&lt;br /&gt;
  float3 lightdir: TEXCOORD2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_light_res vs_light_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj,&lt;br /&gt;
  uniform float3 lightdir&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = float4(position, 1.0f);&lt;br /&gt;
  OUT.screenpos = position * float3(0.5f, 0.5f, 0.0f) + float3(0.5f, 0.5f, 0.0f);//mul(pos, modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(position, modelview);&lt;br /&gt;
  OUT.lightdir = normalize(mul(float4(lightdir, 0.0f), modelview));&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 5: Lichtquellen in das Composite RT rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Render a directional light using a quad&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_4_Composite.Texture);&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(255, 0, 0, 0), 1, 1);  &lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sAlbedo', FRT_1_Albedo.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sPosition', FRT_2_Position.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sNormal', FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.BindEffect;&lt;br /&gt;
  FLightShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  //Render a blue light&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('lightcolor', Ad_ARGB(255, 50, 50, 100));&lt;br /&gt;
  FLightShader.VertexShader.SetParameter('lightdir', AcVector_Normalize(AcVector3(2.0, 2.0, -1.0)));&lt;br /&gt;
  FQuad.BlendMode = bmAdd;&lt;br /&gt;
  FQuad.Draw(nil);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.UnbindEffect;&lt;br /&gt;
  FLightShader.VertexShader.UnbindEffect;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 6: Das Ergebnis Zeichnen'''&amp;lt;br&amp;gt;&lt;br /&gt;
Nun muss nur noch das Ergebnis im &amp;quot;Composite&amp;quot;-RT gezeichnet werden - oder weitere Post-Processing Maßnahmen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
* http://www.gamedev.net/reference/programming/features/defRender/&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Shader]]&lt;br /&gt;
* [[Inferred Lighting]]&lt;br /&gt;
* [[Hintergrundwissen#Render-Pipeline|Render-Pipeline]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24772</id>
		<title>Deferred Shading</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24772"/>
				<updated>2010-03-15T10:58:14Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Deferred Shading */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Deferred Shading =&lt;br /&gt;
&lt;br /&gt;
Der Befriff ''Deferred Shading'' (zu dt. &amp;quot;verzögertes Schattieren&amp;quot;) oder auch ''Deferred Lighting'' (&amp;quot;verzögerte Beleuchtung&amp;quot;) beschreibt eine Technik, mit deren Hilfe die Geometrieverarbeitung von der Lichtberechnung getrennt werden kann. Dies erlaubt hunderte Lichtquellen in einer hoch-komplexen Szene.&lt;br /&gt;
&lt;br /&gt;
== Idee ==&lt;br /&gt;
Um eine Szene mit einem traditionellen Forward-Renderer zu beleuchten, wird normalerweise jedes Objekt der Szene mit den entsprechenden Beleuchtungsparametern der Lichtquelle gezeichnet. Die verschiedenen Ergebnisse werden durch additives Blenden akkumuliert. Für jede Lichtquelle muss also jedes Objekt gezeichnet werden. Angenommen wir haben ''nL'' Lichtquellen und ''nO'' Objekte, dann liegt die Anzahl der Rendercalls bei ''O(nO * nL)''. Natürlich ist es möglich mehrere Lichtquellen in einem Pass (z.B. 8 auf einmal) zu berechnen, aber wir gehen von hunderten Lichtquellen aus, es sind also mehrere Passes notwendig und die Geometrie müsste entsprechend mehrfach verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
''Deferred Shading'' verfolgt einen anderen Ansatz: Anstatt jedes Objekt der Szene für jeden Licht-Pass neu zu renderen, werden zunächst für jeden Pixel alle beleuchtungsrelevanten Daten (z.B. 3D-Position, Normale, Textur-Farbe, etc.) in ein oder mehrere Rendertargets (oftmals auch G-Buffer genannt) geschrieben. Die eigentliche Lichtberechnung kann nun durchgeführt werden indem ein bildschirmfüllendes Quad gerendert wird. Für jeden Pixel werden die Daten aus dem G-Buffer ausgelesen und die Beleuchtung entsprechend dieser Daten berechnet. Die Zahl der Rendercalls liegt bei diesem Ansatz nur noch bei ''O(nO + nL)''. Mit dieser Methode sind hunderte Lichter gleichzeitig in einer hoch-komplexen Szene möglich.&lt;br /&gt;
&lt;br /&gt;
Das Verfahren ist nur dann sinnvoll, wenn man es mit einer sehr komplexen Geometrie und sehr vielen Lichtquellen zu tun hat. Hat man zum Beispiel zwar viele Lichtquellen, aber eine einfache Geometrie, kann bereits ein einfacher Depth-Only-Pass zum initialisieren des Z-Buffers verhindern, dass für unsichtbare Pixel unnötige Lichtberechnungen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Vor-/Nachteile ==&lt;br /&gt;
&lt;br /&gt;
'''Vorteile:'''&lt;br /&gt;
* Viele Lichtquellen bei komplexer Geometrie möglich&lt;br /&gt;
* Postprocessing Effekte (Bloom, Tonemapping, Depth of Field, etc.) können einfach angeschlossen werden, da bereits alle benötigten Informationen aufbereitet vorliegen.&lt;br /&gt;
* Lichtberechnung mit reduzierter Auflösung möglich (sinnvoll z.B. bei FSAA)&lt;br /&gt;
&lt;br /&gt;
'''Nachteile:'''&lt;br /&gt;
* Kein Hardware Anti-Aliasing möglich&lt;br /&gt;
* Transparente Objekte müssen getrennt behandelt werden (Abhilfe schafft [[Inferred Lighting]])&lt;br /&gt;
* Nur auf neueren Grafikkarten mit MRT (Mutliple Render Target) und Floating Point-Textur Support möglich (ab DirectX 9 kompatiblem Grafikchip; OpenGL Version ???)&lt;br /&gt;
&lt;br /&gt;
== Implementierung ==&lt;br /&gt;
Zunächst müssen die Geometriedaten in den (G-Buffer) geschrieben werden. Hierzu benötigen wir drei 16-Bit Float Rendertarget Texturen. Niedrigere Bittiefen sind für Positionsbeschreibung und Normalenvektor zu wenig. Alle Rendertarget-Texturen müssen die gleiche Bittiefe haben. Sind mehrere Rendertargets aktiviert, so wird Hardware-Anti-Aliasing automatisch abgeschaltet. Die folgenden Beispiele sind mit Andorra 2D entwickelt und sollten recht einfach verständlich und nach OpenGL/DirectX umzusetzen sein.&lt;br /&gt;
&lt;br /&gt;
'''Schritt 1: Erstellen der Rendertargets'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
FRT_1_Albedo := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
FRT_1_Albedo.BitDepth := adF64Bit;&lt;br /&gt;
FRT_1_Albedo.SetSize(surfacew, surfaceh);&lt;br /&gt;
FRT_1_Albedo.Filter := atPoint;&lt;br /&gt;
&lt;br /&gt;
FRT_2_Position := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_3_Normal := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_4_Composite := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Schritt 2: Der G-Buffer-Fill Shader (HLSL/Cg)'''&amp;lt;br&amp;gt;&lt;br /&gt;
Der hier gezeigte Shader ist nur ein einfaches Beispiel. Unter anderem kann durch das geschickte Weglassen von Daten (Berechnen von X, Y aus den Z und den Screen-Coordinaten im Lighting-Shader) Bandbreite eingespart werden.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//Fragment Shader&lt;br /&gt;
&lt;br /&gt;
struct fs_res {&lt;br /&gt;
  float4 position: COLOR1;&lt;br /&gt;
  float4 normal: COLOR2;&lt;br /&gt;
  float4 albedo: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
fs_res fs_std_geometry(&lt;br /&gt;
  float3 viewpos: TEXCOORD0,&lt;br /&gt;
  float3 normal: TEXCOORD1&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  fs_res OUT;  &lt;br /&gt;
   &lt;br /&gt;
  OUT.position = float4(viewpos, 1.0f); &lt;br /&gt;
  OUT.normal = float4(normal, 1.0f);&lt;br /&gt;
  OUT.albedo = float4(1.0f, 1.0f, 1.0f , 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Vertex Shader&lt;br /&gt;
&lt;br /&gt;
struct vs_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 viewpos : TEXCOORD0;  &lt;br /&gt;
  float3 normal: TEXCOORD1;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_res vs_std_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  float3 normal: NORMAL,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_res OUT; &lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = mul(float4(position, 1.0f), modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(float4(position, 1.0f), modelview);&lt;br /&gt;
  OUT.normal = normalize(mul(float4(normal, 0.0f), modelview));&lt;br /&gt;
&lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 3: Daten in G-Buffer Rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Activate the G-Buffers&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_1_Albedo.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, FRT_2_Position.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(0, 0, 0, 0), 1, 1);&lt;br /&gt;
&lt;br /&gt;
  //Activate the shaders&lt;br /&gt;
  FTransformShader.FragmentShader.BindEffect;&lt;br /&gt;
  FTransformShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  FScene.Draw(FCamera, nil, 0);&lt;br /&gt;
&lt;br /&gt;
  //Deactivate the shaders&lt;br /&gt;
  FTransformShader.VertexShader.UnbindEffect;&lt;br /&gt;
  FTransformShader.FragmentShader.UnbindEffect;&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, nil);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 4: Einfacher Direktionaler Licht-Shader'''&amp;lt;br&amp;gt;&lt;br /&gt;
Das zur Beleuchtung verwendete Quad liegt in diesem (einfachen) Fall bereits projeziert in normierten Koordinaten vor.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct ps_light_res {&lt;br /&gt;
  float4 color: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
ps_light_res ps_light_geometry(&lt;br /&gt;
  float3 screenpos: TEXCOORD0,&lt;br /&gt;
  float3 viewpos: TEXCOORD1,&lt;br /&gt;
  float3 lightdir: TEXCOORD2,&lt;br /&gt;
  &lt;br /&gt;
  uniform sampler sPosition,&lt;br /&gt;
  uniform sampler sNormal,&lt;br /&gt;
  uniform sampler sAlbedo,&lt;br /&gt;
  uniform float4 lightcolor&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  ps_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  float4 col = tex2D(sAlbedo, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  float4 normal = tex2D(sNormal, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  &lt;br /&gt;
  OUT.color = float4((lightcolor * col * clamp(dot(normal.xyz, lightdir), 0.0f, 1.0f)).rgb, 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct vs_light_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 screenpos: TEXCOORD0;&lt;br /&gt;
  float3 viewpos: TEXCOORD1;&lt;br /&gt;
  float3 lightdir: TEXCOORD2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_light_res vs_light_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj,&lt;br /&gt;
  uniform float3 lightdir&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = float4(position, 1.0f);&lt;br /&gt;
  OUT.screenpos = position * float3(0.5f, 0.5f, 0.0f) + float3(0.5f, 0.5f, 0.0f);//mul(pos, modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(position, modelview);&lt;br /&gt;
  OUT.lightdir = normalize(mul(float4(lightdir, 0.0f), modelview));&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 5: Lichtquellen in das Composite RT rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Render a directional light using a quad&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_4_Composite.Texture);&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(255, 0, 0, 0), 1, 1);  &lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sAlbedo', FRT_1_Albedo.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sPosition', FRT_2_Position.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sNormal', FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.BindEffect;&lt;br /&gt;
  FLightShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  //Render a blue light&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('lightcolor', Ad_ARGB(255, 50, 50, 100));&lt;br /&gt;
  FLightShader.VertexShader.SetParameter('lightdir', AcVector_Normalize(AcVector3(2.0, 2.0, -1.0)));&lt;br /&gt;
  FQuad.BlendMode = bmAdd;&lt;br /&gt;
  FQuad.Draw(nil);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.UnbindEffect;&lt;br /&gt;
  FLightShader.VertexShader.UnbindEffect;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 6: Das Ergebnis Zeichnen'''&amp;lt;br&amp;gt;&lt;br /&gt;
Nun muss nur noch das Ergebnis im &amp;quot;Composite&amp;quot;-RT gezeichnet werden - oder weitere Post-Processing Maßnahmen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
* http://www.gamedev.net/reference/programming/features/defRender/&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Shader]]&lt;br /&gt;
* [[Hintergrundwissen#Render-Pipeline|Render-Pipeline]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24771</id>
		<title>Deferred Shading</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24771"/>
				<updated>2010-03-15T10:55:52Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Vor-/Nachteile */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Deferred Shading =&lt;br /&gt;
&lt;br /&gt;
Der Befriff ''Deferred Shading'' (zu dt. &amp;quot;verzögertes Schattieren&amp;quot;) oder auch ''Deferred Lighting'' (&amp;quot;verzögerte Beleuchtung&amp;quot;) beschreibt eine Technik, mit deren Hilfe eine komplette Lichtquelle mit nur einem einzigen Draw-Call abgebildet werden kann.&lt;br /&gt;
&lt;br /&gt;
== Idee ==&lt;br /&gt;
Um eine Szene mit einem traditionellen Forward-Renderer zu beleuchten, wird normalerweise jedes Objekt der Szene mit den entsprechenden Beleuchtungsparametern der Lichtquelle gezeichnet. Die verschiedenen Ergebnisse werden durch additives Blenden akkumuliert. Für jede Lichtquelle muss also jedes Objekt gezeichnet werden. Angenommen wir haben ''nL'' Lichtquellen und ''nO'' Objekte, dann liegt die Anzahl der Rendercalls bei ''O(nO * nL)''. Natürlich ist es möglich mehrere Lichtquellen in einem Pass (z.B. 8 auf einmal) zu berechnen, aber wir gehen von hunderten Lichtquellen aus, es sind also mehrere Passes notwendig und die Geometrie müsste entsprechend mehrfach verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
''Deferred Shading'' verfolgt einen anderen Ansatz: Anstatt jedes Objekt der Szene für jeden Licht-Pass neu zu renderen, werden zunächst für jeden Pixel alle beleuchtungsrelevanten Daten (z.B. 3D-Position, Normale, Textur-Farbe, etc.) in ein oder mehrere Rendertargets (oftmals auch G-Buffer genannt) geschrieben. Die eigentliche Lichtberechnung kann nun durchgeführt werden indem ein bildschirmfüllendes Quad gerendert wird. Für jeden Pixel werden die Daten aus dem G-Buffer ausgelesen und die Beleuchtung entsprechend dieser Daten berechnet. Die Zahl der Rendercalls liegt bei diesem Ansatz nur noch bei ''O(nO + nL)''. Mit dieser Methode sind hunderte Lichter gleichzeitig in einer hoch-komplexen Szene möglich.&lt;br /&gt;
&lt;br /&gt;
Das Verfahren ist nur dann sinnvoll, wenn man es mit einer sehr komplexen Geometrie und sehr vielen Lichtquellen zu tun hat. Hat man zum Beispiel zwar viele Lichtquellen, aber eine einfache Geometrie, kann bereits ein einfacher Depth-Only-Pass zum initialisieren des Z-Buffers verhindern, dass für unsichtbare Pixel unnötige Lichtberechnungen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Vor-/Nachteile ==&lt;br /&gt;
&lt;br /&gt;
'''Vorteile:'''&lt;br /&gt;
* Viele Lichtquellen bei komplexer Geometrie möglich&lt;br /&gt;
* Postprocessing Effekte (Bloom, Tonemapping, Depth of Field, etc.) können einfach angeschlossen werden, da bereits alle benötigten Informationen aufbereitet vorliegen.&lt;br /&gt;
* Lichtberechnung mit reduzierter Auflösung möglich (sinnvoll z.B. bei FSAA)&lt;br /&gt;
&lt;br /&gt;
'''Nachteile:'''&lt;br /&gt;
* Kein Hardware Anti-Aliasing möglich&lt;br /&gt;
* Transparente Objekte müssen getrennt behandelt werden (Abhilfe schafft [[Inferred Lighting]])&lt;br /&gt;
* Nur auf neueren Grafikkarten mit MRT (Mutliple Render Target) und Floating Point-Textur Support möglich (ab DirectX 9 kompatiblem Grafikchip; OpenGL Version ???)&lt;br /&gt;
&lt;br /&gt;
== Implementierung ==&lt;br /&gt;
Zunächst müssen die Geometriedaten in den (G-Buffer) geschrieben werden. Hierzu benötigen wir drei 16-Bit Float Rendertarget Texturen. Niedrigere Bittiefen sind für Positionsbeschreibung und Normalenvektor zu wenig. Alle Rendertarget-Texturen müssen die gleiche Bittiefe haben. Sind mehrere Rendertargets aktiviert, so wird Hardware-Anti-Aliasing automatisch abgeschaltet. Die folgenden Beispiele sind mit Andorra 2D entwickelt und sollten recht einfach verständlich und nach OpenGL/DirectX umzusetzen sein.&lt;br /&gt;
&lt;br /&gt;
'''Schritt 1: Erstellen der Rendertargets'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
FRT_1_Albedo := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
FRT_1_Albedo.BitDepth := adF64Bit;&lt;br /&gt;
FRT_1_Albedo.SetSize(surfacew, surfaceh);&lt;br /&gt;
FRT_1_Albedo.Filter := atPoint;&lt;br /&gt;
&lt;br /&gt;
FRT_2_Position := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_3_Normal := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_4_Composite := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Schritt 2: Der G-Buffer-Fill Shader (HLSL/Cg)'''&amp;lt;br&amp;gt;&lt;br /&gt;
Der hier gezeigte Shader ist nur ein einfaches Beispiel. Unter anderem kann durch das geschickte Weglassen von Daten (Berechnen von X, Y aus den Z und den Screen-Coordinaten im Lighting-Shader) Bandbreite eingespart werden.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//Fragment Shader&lt;br /&gt;
&lt;br /&gt;
struct fs_res {&lt;br /&gt;
  float4 position: COLOR1;&lt;br /&gt;
  float4 normal: COLOR2;&lt;br /&gt;
  float4 albedo: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
fs_res fs_std_geometry(&lt;br /&gt;
  float3 viewpos: TEXCOORD0,&lt;br /&gt;
  float3 normal: TEXCOORD1&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  fs_res OUT;  &lt;br /&gt;
   &lt;br /&gt;
  OUT.position = float4(viewpos, 1.0f); &lt;br /&gt;
  OUT.normal = float4(normal, 1.0f);&lt;br /&gt;
  OUT.albedo = float4(1.0f, 1.0f, 1.0f , 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Vertex Shader&lt;br /&gt;
&lt;br /&gt;
struct vs_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 viewpos : TEXCOORD0;  &lt;br /&gt;
  float3 normal: TEXCOORD1;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_res vs_std_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  float3 normal: NORMAL,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_res OUT; &lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = mul(float4(position, 1.0f), modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(float4(position, 1.0f), modelview);&lt;br /&gt;
  OUT.normal = normalize(mul(float4(normal, 0.0f), modelview));&lt;br /&gt;
&lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 3: Daten in G-Buffer Rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Activate the G-Buffers&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_1_Albedo.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, FRT_2_Position.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(0, 0, 0, 0), 1, 1);&lt;br /&gt;
&lt;br /&gt;
  //Activate the shaders&lt;br /&gt;
  FTransformShader.FragmentShader.BindEffect;&lt;br /&gt;
  FTransformShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  FScene.Draw(FCamera, nil, 0);&lt;br /&gt;
&lt;br /&gt;
  //Deactivate the shaders&lt;br /&gt;
  FTransformShader.VertexShader.UnbindEffect;&lt;br /&gt;
  FTransformShader.FragmentShader.UnbindEffect;&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, nil);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 4: Einfacher Direktionaler Licht-Shader'''&amp;lt;br&amp;gt;&lt;br /&gt;
Das zur Beleuchtung verwendete Quad liegt in diesem (einfachen) Fall bereits projeziert in normierten Koordinaten vor.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct ps_light_res {&lt;br /&gt;
  float4 color: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
ps_light_res ps_light_geometry(&lt;br /&gt;
  float3 screenpos: TEXCOORD0,&lt;br /&gt;
  float3 viewpos: TEXCOORD1,&lt;br /&gt;
  float3 lightdir: TEXCOORD2,&lt;br /&gt;
  &lt;br /&gt;
  uniform sampler sPosition,&lt;br /&gt;
  uniform sampler sNormal,&lt;br /&gt;
  uniform sampler sAlbedo,&lt;br /&gt;
  uniform float4 lightcolor&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  ps_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  float4 col = tex2D(sAlbedo, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  float4 normal = tex2D(sNormal, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  &lt;br /&gt;
  OUT.color = float4((lightcolor * col * clamp(dot(normal.xyz, lightdir), 0.0f, 1.0f)).rgb, 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct vs_light_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 screenpos: TEXCOORD0;&lt;br /&gt;
  float3 viewpos: TEXCOORD1;&lt;br /&gt;
  float3 lightdir: TEXCOORD2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_light_res vs_light_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj,&lt;br /&gt;
  uniform float3 lightdir&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = float4(position, 1.0f);&lt;br /&gt;
  OUT.screenpos = position * float3(0.5f, 0.5f, 0.0f) + float3(0.5f, 0.5f, 0.0f);//mul(pos, modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(position, modelview);&lt;br /&gt;
  OUT.lightdir = normalize(mul(float4(lightdir, 0.0f), modelview));&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 5: Lichtquellen in das Composite RT rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Render a directional light using a quad&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_4_Composite.Texture);&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(255, 0, 0, 0), 1, 1);  &lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sAlbedo', FRT_1_Albedo.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sPosition', FRT_2_Position.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sNormal', FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.BindEffect;&lt;br /&gt;
  FLightShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  //Render a blue light&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('lightcolor', Ad_ARGB(255, 50, 50, 100));&lt;br /&gt;
  FLightShader.VertexShader.SetParameter('lightdir', AcVector_Normalize(AcVector3(2.0, 2.0, -1.0)));&lt;br /&gt;
  FQuad.BlendMode = bmAdd;&lt;br /&gt;
  FQuad.Draw(nil);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.UnbindEffect;&lt;br /&gt;
  FLightShader.VertexShader.UnbindEffect;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 6: Das Ergebnis Zeichnen'''&amp;lt;br&amp;gt;&lt;br /&gt;
Nun muss nur noch das Ergebnis im &amp;quot;Composite&amp;quot;-RT gezeichnet werden - oder weitere Post-Processing Maßnahmen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
* http://www.gamedev.net/reference/programming/features/defRender/&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Shader]]&lt;br /&gt;
* [[Hintergrundwissen#Render-Pipeline|Render-Pipeline]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24770</id>
		<title>Deferred Shading</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Deferred_Shading&amp;diff=24770"/>
				<updated>2010-03-15T10:51:43Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Idee */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Deferred Shading =&lt;br /&gt;
&lt;br /&gt;
Der Befriff ''Deferred Shading'' (zu dt. &amp;quot;verzögertes Schattieren&amp;quot;) oder auch ''Deferred Lighting'' (&amp;quot;verzögerte Beleuchtung&amp;quot;) beschreibt eine Technik, mit deren Hilfe eine komplette Lichtquelle mit nur einem einzigen Draw-Call abgebildet werden kann.&lt;br /&gt;
&lt;br /&gt;
== Idee ==&lt;br /&gt;
Um eine Szene mit einem traditionellen Forward-Renderer zu beleuchten, wird normalerweise jedes Objekt der Szene mit den entsprechenden Beleuchtungsparametern der Lichtquelle gezeichnet. Die verschiedenen Ergebnisse werden durch additives Blenden akkumuliert. Für jede Lichtquelle muss also jedes Objekt gezeichnet werden. Angenommen wir haben ''nL'' Lichtquellen und ''nO'' Objekte, dann liegt die Anzahl der Rendercalls bei ''O(nO * nL)''. Natürlich ist es möglich mehrere Lichtquellen in einem Pass (z.B. 8 auf einmal) zu berechnen, aber wir gehen von hunderten Lichtquellen aus, es sind also mehrere Passes notwendig und die Geometrie müsste entsprechend mehrfach verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
''Deferred Shading'' verfolgt einen anderen Ansatz: Anstatt jedes Objekt der Szene für jeden Licht-Pass neu zu renderen, werden zunächst für jeden Pixel alle beleuchtungsrelevanten Daten (z.B. 3D-Position, Normale, Textur-Farbe, etc.) in ein oder mehrere Rendertargets (oftmals auch G-Buffer genannt) geschrieben. Die eigentliche Lichtberechnung kann nun durchgeführt werden indem ein bildschirmfüllendes Quad gerendert wird. Für jeden Pixel werden die Daten aus dem G-Buffer ausgelesen und die Beleuchtung entsprechend dieser Daten berechnet. Die Zahl der Rendercalls liegt bei diesem Ansatz nur noch bei ''O(nO + nL)''. Mit dieser Methode sind hunderte Lichter gleichzeitig in einer hoch-komplexen Szene möglich.&lt;br /&gt;
&lt;br /&gt;
Das Verfahren ist nur dann sinnvoll, wenn man es mit einer sehr komplexen Geometrie und sehr vielen Lichtquellen zu tun hat. Hat man zum Beispiel zwar viele Lichtquellen, aber eine einfache Geometrie, kann bereits ein einfacher Depth-Only-Pass zum initialisieren des Z-Buffers verhindern, dass für unsichtbare Pixel unnötige Lichtberechnungen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Vor-/Nachteile ==&lt;br /&gt;
&lt;br /&gt;
'''Vorteile:'''&lt;br /&gt;
* Viele Lichtquellen möglich&lt;br /&gt;
* Postprocessing Effekte (Bloom, Tonemapping, Depth of Field, etc.) können einfach angeschlossen werden, da bereits alle benötigten Informationen aufbereitet vorliegen.&lt;br /&gt;
&lt;br /&gt;
'''Nachteile:'''&lt;br /&gt;
* Kein Hardware Anti-Aliasing möglich&lt;br /&gt;
* Transparente Objekte müssen getrennt behandelt werden&lt;br /&gt;
* Nur auf neueren Grafikkarten mit MRT (Mutliple Render Target) und Floating Point-Textur Support möglich (ab DirectX 9 kompatiblem Grafikchip; OpenGL Version ???)&lt;br /&gt;
&lt;br /&gt;
== Implementierung ==&lt;br /&gt;
Zunächst müssen die Geometriedaten in den (G-Buffer) geschrieben werden. Hierzu benötigen wir drei 16-Bit Float Rendertarget Texturen. Niedrigere Bittiefen sind für Positionsbeschreibung und Normalenvektor zu wenig. Alle Rendertarget-Texturen müssen die gleiche Bittiefe haben. Sind mehrere Rendertargets aktiviert, so wird Hardware-Anti-Aliasing automatisch abgeschaltet. Die folgenden Beispiele sind mit Andorra 2D entwickelt und sollten recht einfach verständlich und nach OpenGL/DirectX umzusetzen sein.&lt;br /&gt;
&lt;br /&gt;
'''Schritt 1: Erstellen der Rendertargets'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
FRT_1_Albedo := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
FRT_1_Albedo.BitDepth := adF64Bit;&lt;br /&gt;
FRT_1_Albedo.SetSize(surfacew, surfaceh);&lt;br /&gt;
FRT_1_Albedo.Filter := atPoint;&lt;br /&gt;
&lt;br /&gt;
FRT_2_Position := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_3_Normal := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
FRT_4_Composite := TAdRenderTargetTexture.Create(FDraw);&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Schritt 2: Der G-Buffer-Fill Shader (HLSL/Cg)'''&amp;lt;br&amp;gt;&lt;br /&gt;
Der hier gezeigte Shader ist nur ein einfaches Beispiel. Unter anderem kann durch das geschickte Weglassen von Daten (Berechnen von X, Y aus den Z und den Screen-Coordinaten im Lighting-Shader) Bandbreite eingespart werden.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//Fragment Shader&lt;br /&gt;
&lt;br /&gt;
struct fs_res {&lt;br /&gt;
  float4 position: COLOR1;&lt;br /&gt;
  float4 normal: COLOR2;&lt;br /&gt;
  float4 albedo: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
fs_res fs_std_geometry(&lt;br /&gt;
  float3 viewpos: TEXCOORD0,&lt;br /&gt;
  float3 normal: TEXCOORD1&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  fs_res OUT;  &lt;br /&gt;
   &lt;br /&gt;
  OUT.position = float4(viewpos, 1.0f); &lt;br /&gt;
  OUT.normal = float4(normal, 1.0f);&lt;br /&gt;
  OUT.albedo = float4(1.0f, 1.0f, 1.0f , 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//Vertex Shader&lt;br /&gt;
&lt;br /&gt;
struct vs_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 viewpos : TEXCOORD0;  &lt;br /&gt;
  float3 normal: TEXCOORD1;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_res vs_std_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  float3 normal: NORMAL,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_res OUT; &lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = mul(float4(position, 1.0f), modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(float4(position, 1.0f), modelview);&lt;br /&gt;
  OUT.normal = normalize(mul(float4(normal, 0.0f), modelview));&lt;br /&gt;
&lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 3: Daten in G-Buffer Rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Activate the G-Buffers&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_1_Albedo.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, FRT_2_Position.Texture);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(0, 0, 0, 0), 1, 1);&lt;br /&gt;
&lt;br /&gt;
  //Activate the shaders&lt;br /&gt;
  FTransformShader.FragmentShader.BindEffect;&lt;br /&gt;
  FTransformShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  FScene.Draw(FCamera, nil, 0);&lt;br /&gt;
&lt;br /&gt;
  //Deactivate the shaders&lt;br /&gt;
  FTransformShader.VertexShader.UnbindEffect;&lt;br /&gt;
  FTransformShader.FragmentShader.UnbindEffect;&lt;br /&gt;
&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(2, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(1, nil);&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, nil);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 4: Einfacher Direktionaler Licht-Shader'''&amp;lt;br&amp;gt;&lt;br /&gt;
Das zur Beleuchtung verwendete Quad liegt in diesem (einfachen) Fall bereits projeziert in normierten Koordinaten vor.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct ps_light_res {&lt;br /&gt;
  float4 color: COLOR0;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
ps_light_res ps_light_geometry(&lt;br /&gt;
  float3 screenpos: TEXCOORD0,&lt;br /&gt;
  float3 viewpos: TEXCOORD1,&lt;br /&gt;
  float3 lightdir: TEXCOORD2,&lt;br /&gt;
  &lt;br /&gt;
  uniform sampler sPosition,&lt;br /&gt;
  uniform sampler sNormal,&lt;br /&gt;
  uniform sampler sAlbedo,&lt;br /&gt;
  uniform float4 lightcolor&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  ps_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  float4 col = tex2D(sAlbedo, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  float4 normal = tex2D(sNormal, float2(screenpos.x, -screenpos.y));&lt;br /&gt;
  &lt;br /&gt;
  OUT.color = float4((lightcolor * col * clamp(dot(normal.xyz, lightdir), 0.0f, 1.0f)).rgb, 1.0f);&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct vs_light_res {&lt;br /&gt;
  float4 pos: POSITION;&lt;br /&gt;
  float3 screenpos: TEXCOORD0;&lt;br /&gt;
  float3 viewpos: TEXCOORD1;&lt;br /&gt;
  float3 lightdir: TEXCOORD2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
vs_light_res vs_light_geometry(&lt;br /&gt;
  float3 position : POSITION,&lt;br /&gt;
  uniform float4x4 modelview,&lt;br /&gt;
  uniform float4x4 modelviewproj,&lt;br /&gt;
  uniform float3 lightdir&lt;br /&gt;
)&lt;br /&gt;
{&lt;br /&gt;
  vs_light_res OUT;&lt;br /&gt;
  &lt;br /&gt;
  OUT.pos = float4(position, 1.0f);&lt;br /&gt;
  OUT.screenpos = position * float3(0.5f, 0.5f, 0.0f) + float3(0.5f, 0.5f, 0.0f);//mul(pos, modelviewproj);&lt;br /&gt;
  OUT.viewpos = mul(position, modelview);&lt;br /&gt;
  OUT.lightdir = normalize(mul(float4(lightdir, 0.0f), modelview));&lt;br /&gt;
  &lt;br /&gt;
  return OUT;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 5: Lichtquellen in das Composite RT rendern'''&lt;br /&gt;
&amp;lt;source lang=&amp;quot;delphi&amp;quot;&amp;gt;&lt;br /&gt;
  //Render a directional light using a quad&lt;br /&gt;
  FDraw.AdAppl.SetRenderTarget(0, FRT_4_Composite.Texture);&lt;br /&gt;
  FDraw.AdAppl.Viewport := AdRect(0, 0, surfacew, surfaceh);&lt;br /&gt;
  FDraw.AdAppl.ClearSurface(AdRect(0, 0, surfacew, surfaceh), [alColorBuffer, alZBuffer,&lt;br /&gt;
    alStencilBuffer], Ad_ARGB(255, 0, 0, 0), 1, 1);  &lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sAlbedo', FRT_1_Albedo.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sPosition', FRT_2_Position.Texture);&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('sNormal', FRT_3_Normal.Texture);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.BindEffect;&lt;br /&gt;
  FLightShader.VertexShader.BindEffect;&lt;br /&gt;
&lt;br /&gt;
  //Render a blue light&lt;br /&gt;
  FLightShader.FragmentShader.SetParameter('lightcolor', Ad_ARGB(255, 50, 50, 100));&lt;br /&gt;
  FLightShader.VertexShader.SetParameter('lightdir', AcVector_Normalize(AcVector3(2.0, 2.0, -1.0)));&lt;br /&gt;
  FQuad.BlendMode = bmAdd;&lt;br /&gt;
  FQuad.Draw(nil);&lt;br /&gt;
&lt;br /&gt;
  FLightShader.FragmentShader.UnbindEffect;&lt;br /&gt;
  FLightShader.VertexShader.UnbindEffect;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Schritt 6: Das Ergebnis Zeichnen'''&amp;lt;br&amp;gt;&lt;br /&gt;
Nun muss nur noch das Ergebnis im &amp;quot;Composite&amp;quot;-RT gezeichnet werden - oder weitere Post-Processing Maßnahmen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
* http://www.gamedev.net/reference/programming/features/defRender/&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Shader]]&lt;br /&gt;
* [[Hintergrundwissen#Render-Pipeline|Render-Pipeline]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Shadersammlung&amp;diff=24712</id>
		<title>Shadersammlung</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Shadersammlung&amp;diff=24712"/>
				<updated>2010-02-27T11:14:35Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Beleuchtung und Texturen */ 2 Sterne für Bumpmapping da Normalmap benötigt wird.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Was sind Shader?==&lt;br /&gt;
Die traditionelle [[Feste_Funktionspipeline|Funktionspipeline]] der OpenGL ist sehr beschränkt, neuere [[Algorithmus|Algorithmen]] erfordern eine viel höhere Flexibilität. [[Shader]] sind kleine Programme die es erlauben Teile der Funktionspipeline völlig frei zu programmieren. Dies bietet völlig neue Möglichkeiten. Auf der einen Seite sind Shader für Einsteiger eine gewisse Hürde, auf der anderen Seite können sie viele Dinge aber auch enorm vereinfachen. Aus diesem Grund wurde in OpenGL 3.1 sowie OpenGL ES 2.0 die feste Funktionspipeline entfernt, so dass Shader dort verpflichtend sind.&lt;br /&gt;
&lt;br /&gt;
Der Artikel [[Shader]] gibt einen ausführlichen Überblick zum Thema sowie eine [[Shader#Wie_geht_es_nun_weiter.3F|Linksammlung]] für den Einstieg.&lt;br /&gt;
&lt;br /&gt;
==Shader hinzufügen==&lt;br /&gt;
Die Shadersammlung braucht deine Hilfe um zu wachsen. Wenn du einen Shader bereitstellen könntest wäre dies ein toller Dienst für die Community.&lt;br /&gt;
&lt;br /&gt;
Wer einen Shader zur Shadersammlung hinzufügen möchte, findet am Ende dieses Seite eine [[#Vorlage|Vorlage]].&lt;br /&gt;
&lt;br /&gt;
==Schwierigkeitsgrad==&lt;br /&gt;
Es ist nicht leicht einen Schwierigkeitsgrad für Shader anzugeben, trotzdem haben wir dies einmal versucht. Der Schwierigkeitsgrad kann zumindest für Einsteiger einen groben Hinweis geben was man sich vielleicht mal anschauen könnte.&lt;br /&gt;
{|{{Prettytable_B1}} style=&amp;quot;margin-left:50px;&amp;quot;&lt;br /&gt;
| {{TableHead}} | {{ShaderLevel|1}}&lt;br /&gt;
| Der Shader ist für Einsteiger geeignet. Um den Shader herum wird keine zusätzliche Infrastruktur benötigt.&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}} | {{ShaderLevel|2}}&lt;br /&gt;
| Der Shader ist für Einsteiger geeignet, benötigt aber etwas zusätzliche Infrastruktur. Etwa benötigt ein Post-Processing-Shader ein [[FBO|FrameBufferObject]], für [[Bumpmapping]] wird eine [[Normalmap]] benötigt.&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}} | {{ShaderLevel|3}}&lt;br /&gt;
| Mittlerer Schwierigkeitsgrad. Der Shader ist etwas komplizierter und/oder benötigt viel zusätzliche Infrastruktur.&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}} | {{ShaderLevel|4}}&lt;br /&gt;
| Hoher Schwierigkeitsgrad. Der Shader ist kompliziert und/oder verwendet intensiv Extensions.&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}} | {{ShaderLevel|5}}&lt;br /&gt;
| Extremer Schwierigkeitsgrad...alles was es sonst noch gibt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==GLSL-Shader==&lt;br /&gt;
Die hier gelisteten Shader sind in Kategorien aufgeteilt. Weitere Kategorien dürfen bei Bedarf einfach ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
===Beleuchtung und Texturen===&lt;br /&gt;
Diese Kategorie umfasst alles vom einfachen Fixed-Function-Pipeline-Ersatz über PerPixel-Licht und Bumpmapping bis zu ShadowMaps. Der Shader-Einsteiger wird in dieser Kategorie sicherlich fündig.&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
| {{TableHead}}  | [[shader_verysimple|Verysimple]] von [[Benutzer:damadmax|damadmax]] {{ShaderLevel|1}}&lt;br /&gt;
| {{Table150R2}} style=&amp;quot;text-align:center;&amp;quot; | (kein Bild vorhanden)&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Ein einfacher Shader, welcher eine Textur an Vertices bindet. Dieser Shader baut damit das Standardverhalten der festen Pipeline nach. {{ShaderLink|shader_verysimple}}&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_PerPixelLighting|Per Pixel-Beleuchtung]] von [[Benutzer:Ireyon|Ireyon]] {{ShaderLevel|1}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:shader_ppl.png|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Shader, der Per Pixel beleuchtet. {{ShaderLink|shader_PerPixelLighting}}&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_PerPixel_Lighting2|Per Pixel-Beleuchtung2]] von [[Benutzer:Olee|Olee]] {{ShaderLevel|1}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:PPLShader.jpg|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Beleuchtet Flächen nach Pixeln anstatt wie OpenGl-Licht je Vertex. Erlaubt unendlich weit entferntes Licht und beachtet [[glFog]]. {{ShaderLink|shader_PerPixel_Lighting2}}&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_ConeVolumeShadow|Kegelvolumen-Schatten ]] von [[Benutzer:Coolcat|Coolcat]] {{ShaderLevel|3}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:shader_ConeVolumeShadow2.jpg|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Spezialshader für realistische Schatten einer Kugellichtquelle. Kugellichtquellen erzeugen im Gegensatz zu den üblichen Punktlichtquellen weiche Schatten. Dieser Ansatz ist im Vergleich extrem schnell, da er nur eine Erweiterung des gewöhnlichen Per-Pixel-Lighting darstellt. Allerdings funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Schatten werfenden Objekten in der Szene um Kugeln handelt. {{ShaderLink|shader_ConeVolumeShadow}}&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_Bumpmapping|BumpmappingShader]] von [[Benutzer:Bergmann89|Bergmann89]] {{ShaderLevel|2}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:Bumpmapping.JPG|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | BumpmappingShader auf der Basis des [[shader_PerPixelLighting|Per Pixel-Beleuchtung]]-Shaders {{ShaderLink|shader_Bumpmapping}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Post-Processing===&lt;br /&gt;
Post-Processing-Shader zeichnen sich dadurch aus, dass sie auf die gesamte Szene angewendet werden &amp;lt;u&amp;gt;nachdem&amp;lt;/u&amp;gt; diese bereits gerendert wurde. Üblicherweise wird die Szene zuerst mit einem [[FBO|FramebufferObject]] (FBO) in eine Textur gerendert. Anschließend wird diese Textur auf ein bildschirmfüllendes Quad gespannt und mit dem Post-Processing-Shader gerendert.&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
| {{TableHead}}  | [[shader_sepia|Sepia-Shader]] von [[Benutzer:Markus|Markus]] {{ShaderLevel|2}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:shader_sepia_nachher.jpg|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Post-Processing-Shader, welcher einen Sepia-Farbfilter auf die Szene anwendet. {{ShaderLink|shader_sepia}}&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_radial_blur|Radial Blur]] von [[Benutzer:Markus|Markus]] {{ShaderLevel|2}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:shader_radial_blur_nachher.jpg|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Post-Processing-Shader, welcher die Szene ausgehend von einem beliebigen Punkt verwischt. {{ShaderLink|shader_radial_blur}}&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_blur2|Blur-Shader]] von [[Benutzer:Skeptiker|Skeptiker]] und [[Benutzer:Coolcat|Coolcat]] {{ShaderLevel|3}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:Shader_blur_seerose_blured_small.jpg|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Post-Processing-Shader, welcher die Szene insgesamt verwischt. Dieser Shader kann beispielsweise genutzt werden um einen Glow-Effekt zu erreichen. {{ShaderLink|shader_blur2}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Geometrie===&lt;br /&gt;
Hier finden sich Shader die die Geometrie entweder komplett neu erzeugen oder stark verändern. Es muss nicht unbedingt Geometryshader oder Instancing sein, auch mit dem Vertexshader kann man schon viel anstellen. Ein klassisches Beispiel ist die Mesh-Animation.&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
| {{TableHead}}  | [[shader_Terrain_GPU4|Heightmap-Terrain]] von [[Benutzer:Coolcat|Coolcat]] {{ShaderLevel|3}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:Terrain.jpg|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Shader für ein Heightmap-Terrain der ShaderModel 4.0 Features nutzt. Das Terrain wird zur Laufzeit fast vollständig aus der Heightmap generiert, was ordentlich Speicherplatz spart. Im Fragmentshader werden drei Texturlayer mit Hilfe einer Alphamap interpoliert {{ShaderLink|shader_Terrain_GPU4}}&lt;br /&gt;
&lt;br /&gt;
'''Extensions:'''&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_EXT_gpu_shader4]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_Instancing|Instancing-Shader]] von [[Benutzer:Coolcat|Coolcat]] {{ShaderLevel|4}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:shader_Instancing.jpg|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Dieser Shader zeigt die Benutzung von [[GL_ARB_draw_instanced]]. Ein häufiger Anwendungsfall ist das rendern vieler gleichartiger Objekte. Die Extension beschleunigt derartige Anwendungen, in dem die Anzahl der API-Aufrufe und die Menge redundanter Daten reduziert wird. {{ShaderLink|shader_Instancing}}&lt;br /&gt;
&lt;br /&gt;
'''Extensions:'''&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_ARB_draw_instanced]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_EXT_gpu_shader4]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_EXT_texture_buffer_object]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_ARB_texture_float]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_tesselation|Tesselation-Shader]] von [[Benutzer:dj3hut1|dj3hut1]] {{ShaderLevel|4}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:Tesselation_s.png|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Dieser Shader nutzt einen Geometry Shader um ein Dreieck zu tesselieren. {{ShaderLink|shader_tesselation}}&lt;br /&gt;
&lt;br /&gt;
'''Extensions:'''&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_ARB_geometry_shader4]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_normal_debug|Normal Debugging-Shader]] von [[Benutzer:dj3hut1|dj3hut1]] {{ShaderLevel|4}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:Normal_debug_s.png|113px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Nützlicher Shader um ohne viel Aufwand die Normalen eines Modells anzuzeigen. {{ShaderLink|shader_normal_debug}}&lt;br /&gt;
&lt;br /&gt;
'''Extensions:'''&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_ARB_geometry_shader4]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Partikel===&lt;br /&gt;
Natürlich kann man auch bei einem [[Partikelsystem]] Shader einsetzen. Mit etwas Geschick kann man die gesamte Berechnung sogar vollständig auf die Grafikkarte verlagern um die CPU zu entlasten.&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
| {{TableHead}}  | [[GLSL_Partikel_2|GPU Partikelsystem]] von [[Benutzer:Coolcat|Coolcat]] {{ShaderLevel|5}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:Partikel-Gravitation.jpg|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Ein GPU-Partikelsystem welches insbesondere auf SM4 Features wie Geometryshader und Transform-Feedback setzt. So ist auch eine Partikel-Partikel-Interaktion mit über 200.000 Partikeln ist kein Problem. {{ShaderLink|GLSL_Partikel_2}}&lt;br /&gt;
&lt;br /&gt;
'''Extensions:'''&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_EXT_geometry_shader4]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_NV_transform_feedback]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_EXT_gpu_shader4]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_EXT_texture_buffer_object]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_ARB_texture_float]]&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_EXT_texture_integer]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[GLSL_Partikel|GPU Partikelsystem für alte Hardware]] von [[Benutzer:Oc2k1|Oc2k1]] {{ShaderLevel|5}}&lt;br /&gt;
| {{Table150R2}} style=&amp;quot;text-align:center;&amp;quot; | (kein Bild vorhanden)&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Ebenfalls ein GPU-Partikelsystem, welches aber auf FramebufferObjects und Fragmentshader setzt. Damit sollte es auch auf älterer Hardware funktionieren. {{ShaderLink|GLSL_Partikel}}&lt;br /&gt;
&lt;br /&gt;
'''Extensions:'''&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_ARB_texture_float]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Sonstiges===&lt;br /&gt;
Hier kommt alles rein was sonst nirgendwo rein passt und eine neue Kategorie nicht lohnenswert ist.&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
| {{TableHead}}  | [[shader_game_of_life|Game of Life]] von [[Benutzer:dj3hut1|dj3hut1]] {{ShaderLevel|3}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:Game_of_life_s.png|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Simuliert das altbekannte 'Game of Life' im Shader. {{ShaderLink|shader_game_of_life}}&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_Zufallsgenerator|Pseudozufallsgenerator]] von [[Benutzer:Coolcat|Coolcat]] {{ShaderLevel|4}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:shader_Zufallsgenerator.png|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Generierung von Pseudozufallszahlen im Shader mit [[GL_EXT_gpu_shader4]]. Außerdem eine Beispielanwendung: Ein reproduzierbares pesudozufälliges Verwirbelfeld für ein GPU-Partikelsystem. {{ShaderLink|shader_Zufallsgenerator}}&lt;br /&gt;
&lt;br /&gt;
'''Extensions:'''&amp;lt;br&amp;gt;&lt;br /&gt;
[[GL_EXT_gpu_shader4]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Non-GLSL-Shader==&lt;br /&gt;
In diesem Abschnitt findet ihr Shader die mit einer anderen Technik als GLSL geschrieben wurden.&lt;br /&gt;
&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
| {{TableHead}}  | [[shader_diffuse_bumpmapping(Cg)|Diffuse Bumpmapping (NVIDIA-Cg)]] von [[Benutzer:igel457|igel457]] {{ShaderLevel|2}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:shader_diffuse_bumpmapping_cg.jpg|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Wendet diffuses Bumpmapping auf eine beliebige Oberfläche an. {{ShaderLink|shader_diffuse_bumpmapping(Cg)}}&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_surface_scattering(ARB)|Surface Scattering (ARB)]] von [[Benutzer:dj3hut1|dj3hut1]] {{ShaderLevel|3}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:Scattering_s.jpg|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Der Shader bestimmt die Distanz, die Licht durch ein Material zurücklegt. Auf diese Weise wird die Lichtdurchlässigkeit bestimmt. {{ShaderLink|shader_surface_scattering(ARB)}}&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_texturing(ARB)|Texturing (ARB)]] von [[Benutzer:dj3hut1|dj3hut1]] {{ShaderLevel|1}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:Texarb_textured_s.png|166px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Demonstriert eine einfache Texturierung. {{ShaderLink|shader_texturing(ARB)}}&lt;br /&gt;
|-&lt;br /&gt;
| {{TableHead}}  | [[shader_phong_per_pixel(ARB)|Phong per Pixel (ARB)]] von [[Benutzer:dj3hut1|dj3hut1]] {{ShaderLevel|1}}&lt;br /&gt;
| {{Table150R2}} | [[Bild:Phong_arb_s.png|150px|150px]]&lt;br /&gt;
|-&lt;br /&gt;
| {{TableCell}}  | Per-Pixel-Beleuchtung. {{ShaderLink|shader_phong_per_pixel(ARB)}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Vorlage==&lt;br /&gt;
Die Shadersammlung braucht deine Hilfe um zu wachsen. Wenn du einen Shader bereitstellen könntest wäre dies ein toller Dienst für die Community.&lt;br /&gt;
&lt;br /&gt;
Folgende Schritte müssen gemacht werden um einen Shader einzutragen:&lt;br /&gt;
# Erstelle mit Hilfe der [[Shaderartikelvorlage]] einen neuen Artikel. Dazu den wiki-Code der Vorlage einfach kopieren und in den neuen Artikel einfügen. Der Artikelname sollte mit dem Präfix &amp;lt;tt&amp;gt;shader_&amp;lt;/tt&amp;gt; beginnen, z.B. &amp;lt;tt&amp;gt;shader_Wasser2d&amp;lt;/tt&amp;gt; für einen 2D Wassershader.&lt;br /&gt;
# Da Shader zu 90% optische Effekte bewirken, sollte (mindestens) ein aussagekräftiges Beispielbild im Shaderartikel hinterlegt werden. Eines der Bilder sollte dann auch hier in der Liste verlinkt werden.&lt;br /&gt;
# Füge deinen Shader in die Shadersammlung ein. Verwende dazu dieses Template:&amp;lt;br&amp;gt;&amp;lt;tt&amp;gt; |-&amp;lt;/tt&amp;gt;&amp;lt;br&amp;gt;&amp;lt;tt&amp;gt; | &amp;lt;nowiki&amp;gt;{{TableHead}}  | [[shader_Wasser2d|2D Wassershader]] von [[Benutzer:DeinName|DeinName]]&amp;lt;/nowiki&amp;gt;&amp;lt;/tt&amp;gt;&amp;lt;br&amp;gt;&amp;lt;tt&amp;gt; | &amp;lt;nowiki&amp;gt;{{Table150R2}} | [[Bild:Wassershader2D.jpg|150px|150px]]&amp;lt;/nowiki&amp;gt;&amp;lt;/tt&amp;gt;&amp;lt;br&amp;gt;&amp;lt;tt&amp;gt; |-&amp;lt;/tt&amp;gt;&amp;lt;br&amp;gt;&amp;lt;tt&amp;gt; | &amp;lt;nowiki&amp;gt;{{TableCell}}&amp;lt;/nowiki&amp;gt;  | Einige Sätze zur Beschreibung des Shaders. &amp;lt;nowiki&amp;gt;{{ShaderLink|shader_Wasser2d}}&amp;lt;/nowiki&amp;gt;&amp;lt;/tt&amp;gt;&amp;lt;br&amp;gt;&amp;lt;tt&amp;gt; &amp;lt;/tt&amp;gt;&amp;lt;br&amp;gt;&amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;'''Extensions:'''&amp;lt;br&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/tt&amp;gt;&amp;lt;br&amp;gt;&amp;lt;tt&amp;gt;Liste von notwendigen Extensions.&amp;lt;/tt&amp;gt;&amp;lt;br&amp;gt;&amp;lt;tt&amp;gt; |-&amp;lt;/tt&amp;gt;&lt;br /&gt;
# Keine Angst, es muss nicht alles auf Anhieb perfekt sein.&lt;br /&gt;
&lt;br /&gt;
Falls du Fragen oder Probleme mit der Vorlage hast, im [[DGL-Chat]] oder [http://www.delphigl.com/forum/ Forum] hilft man immer gerne.&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=GLSL_Partikel_2&amp;diff=24706</id>
		<title>GLSL Partikel 2</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=GLSL_Partikel_2&amp;diff=24706"/>
				<updated>2010-02-20T16:05:33Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Emitter */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Partikel-Hovercraft.jpg|thumb|256px|Hovercraft wirbelt Staub auf.]]&lt;br /&gt;
Der [[GLSL_Partikel|klassische Ansatz]] eines GPU Partikelsystems verwendet eine oder mehrere Texturen um die Partikel zu speichern. Zur Aktualisierung wird ein Quad in Größe dieser Textur gerendert. Der [[Fragmentshader]] liest die alten Daten aus einem Backbuffer und schreibt die aktualisierten Partikel in den Framebuffer. Dieses Verfahren ist an sich sehr effizient, hat aber einige Nachteile:&lt;br /&gt;
* Es ist sehr kompliziert effizient neue Partikel von der CPU zu emittieren. Die CPU muss immer wissen wo sich aktive Partikel in der Textur befinden. Sind die Lebensdauern der Partikel unterschiedlich (oder gar zufällig) wird dies zu einem echten Problem. In der Regel erfordert dies zumindest die Berechnung der Lebendauer zusätzlich auch auf der CPU durchzuführen, was natürlich nicht der Sinn eines GPU Partikelsystems ist.&lt;br /&gt;
* Da die Partikelanordnung wie gerade beschrieben fragmentiert, muss beim rendern der Partikel immer die vollständige Textur verarbeitet werden, auch wenn eigentlich nur wenige Partikel tatsächlich aktiv sind. Außerdem ist zum rendern ein großes [[VBO]] mit Dummy-Partikeln erforderlich, die eigentlich keine Daten enthalten.&lt;br /&gt;
* Sollen die Partikel zum Beispiel nach der Entfernung zur Kamera sortiert werden, ist dies sehr aufwendig. Auf der GPU verwendet man üblicherweise Odd-Even-Mergesort oder Bitonic-Sort. Beide Algorithmen vergleichen jeweils zwei Elemente und führen dann ggf. einen Tausch (Swap) dieser Elemente aus. Im Fragmentshader lässt sich jedoch die Position eines Fragments nicht mehr nachträglich ändern. Eine Implementierung im Fragmentshader erfordert also, dass jeder Vergleich zweimal durchgeführt wird. Das bedeutet auch, dass jeder Texturzugriff zweimal durchgeführt werden muss. Zudem erfordern beide Algorithmen sehr viele Passes. Für eine Million Partikel (1024²) sind so etwa 210 Passes erforderlich. Dies ist auch bei einer Verteilung über mehrere Frames nicht mehr sinnvoll in Echtzeit durchführbar.&lt;br /&gt;
Durch die Benutzung der Features aktueller Grafikhardware wie dem [[Geometryshader]], [[Transform-Feedback]], [[Instancing]] sowie diverser weiterer Features des Shader Model 4.0 lässt sich hier viel optimieren. Dieser Artikel setzt die Kenntnis dieser Funktionen voraus. Desweiteren sollte man natürlich wissen wie ein [[Partikelsystem|(state-preserving) Partikelsystem]] funktioniert, also zum Beispiel etwas mit den Begriffen [[Partikelsystem:Emitter|Emitter]] und [[Partikelsystem:Forcefield|Forcefield]] anfangen können.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
Ich verwende hier neben OpenGL 2.0 auch zahlreiche Extensions. Wenn man das Grundprinzip verstanden hat sollte es kein Problem sein diese Extensions durch eventuell verfügbare aktuellere Extensions zu ersetzen.&lt;br /&gt;
* [[GL_ARB_texture_float]]&lt;br /&gt;
* [[GL_EXT_texture_integer]]&lt;br /&gt;
* [[GL_EXT_texture_buffer_object]]&lt;br /&gt;
* [[GL_EXT_gpu_shader4]]&lt;br /&gt;
* [[GL_NV_transform_feedback]]&lt;br /&gt;
* [[GL_EXT_geometry_shader4]]&lt;br /&gt;
Zum speichern der Partikeldaten verwende ich mehrere Texturbuffer-Objects (TBO).&lt;br /&gt;
* ein 4x32bit-float Buffer zum speichern der Position und Größe des Partikels (''GL_RGBA32F_ARB'')&lt;br /&gt;
* ein weiterer 4x32bit-float Buffer zum speichern der Velocity und Lebenszeit des Partikels (''GL_RGBA32F_ARB'')&lt;br /&gt;
* ein 32bit-unsigned Buffer zum speichern des aktuellen Zufallsseed (''GL_LUMINANCE32UI_EXT'')&lt;br /&gt;
Alle Partikel werden durch Emitter erzeugt. Da uns später immer die genaue Anzahl der aktiven Partikel bekannt sein wird, müssen die Buffer nicht initialisiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
Das wichtigste an einem Partikelsystem ist immer der Update-Schritt. Daher fangen wir auch damit an. Auch wenn wir erst später erfahren wie neu erzeugte Partikel eigentlich in den Buffer kommen, nehmen wir zunächst einmal an die Buffer wären mit aktiven Partikeln gefüllt.&lt;br /&gt;
&lt;br /&gt;
Eine Grafikkarte kann nicht aus einem Buffer lesen und gleichzeitig auch hinein schreiben. Wir benötigen also jeden Buffer doppelt: Einen Inputbuffer aus dem wir als VBO bzw. TBO lesen, während wir in den Outputbuffer via Transform-Feedback schreiben. Nach jedem Updateschritt werden die Rollen der Buffer getauscht.&lt;br /&gt;
&lt;br /&gt;
Zunächst interpretieren wir den Position-Inputbuffer als Vertexbuffer und rendern sämtliche Partikel. Der Vertexshader ist trivial, denn die eigentliche Arbeit verrichtet der Geometryshader. Clipping und alles was danach in der [[:Bild:Pipeline.png|Pipeline]] kommt wird nicht benötigt. Also insbesondere der Rasterizer und der Fragmentshader. Darum schalten wird diesen unnötigen Kram mit {{INLINE_CODE|glEnable(GL_RASTERIZER_DISCARD_NV)}} einfach ab. Man sollte natürlich nicht vergessen das ganze später wieder einzuschalten.&lt;br /&gt;
&lt;br /&gt;
'''Vertexshader''':&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;void main() {&lt;br /&gt;
	gl_Position = gl_Vertex;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Geometryshader''':&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;#extension GL_EXT_gpu_shader4 : enable&lt;br /&gt;
#extension GL_EXT_geometry_shader4: enable&lt;br /&gt;
&lt;br /&gt;
uniform samplerBuffer tboVelocity;&lt;br /&gt;
uniform usamplerBuffer tboRandom;&lt;br /&gt;
uniform float timeElapsed;&lt;br /&gt;
&lt;br /&gt;
varying out vec4 velocity_out;&lt;br /&gt;
varying out unsigned int seed;&lt;br /&gt;
&lt;br /&gt;
const vec3 wind = vec3(1.0, 1.0, 2.0);&lt;br /&gt;
&lt;br /&gt;
vec3 constField() {&lt;br /&gt;
	return timeElapsed * wind;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
vec3 dampField(vec3 velocity, float strength) {&lt;br /&gt;
	return -velocity*strength*timeElapsed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
vec3 noiseField(float strength) {&lt;br /&gt;
	vec3 result;&lt;br /&gt;
	seed = (seed * 1103515245u + 12345u); result.x = float(seed);&lt;br /&gt;
	seed = (seed * 1103515245u + 12345u); result.y = float(seed);&lt;br /&gt;
	seed = (seed * 1103515245u + 12345u); result.z = float(seed);&lt;br /&gt;
	return timeElapsed * strength * ((result / 4294967296.0) - vec3(0.5,0.5,0.5));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	// get all particle data&lt;br /&gt;
	vec3 position = gl_PositionIn[0].xyz;&lt;br /&gt;
	float size = gl_PositionIn[0].w;&lt;br /&gt;
&lt;br /&gt;
	vec4 tmp = texelFetchBuffer(tboVelocity, gl_PrimitiveIDIn);&lt;br /&gt;
	vec3 velocity = tmp.xyz;&lt;br /&gt;
	float lifetime = tmp.w;&lt;br /&gt;
&lt;br /&gt;
	// update lifetime and discard dead particles&lt;br /&gt;
	lifetime -= timeElapsed;&lt;br /&gt;
	if (lifetime &amp;lt;= 0.0) { return; }&lt;br /&gt;
&lt;br /&gt;
	// update position and size&lt;br /&gt;
	position += velocity * timeElapsed;&lt;br /&gt;
	size += 0.3 * size * timeElapsed;&lt;br /&gt;
	&lt;br /&gt;
	// read current random seed&lt;br /&gt;
	seed = texelFetchBuffer(tboRandom, gl_PrimitiveIDIn).x;&lt;br /&gt;
&lt;br /&gt;
	// update velocity by applying force fields&lt;br /&gt;
	velocity += dampField(velocity, 0.5);&lt;br /&gt;
	velocity += constField();&lt;br /&gt;
	velocity += noiseField(1.0);&lt;br /&gt;
	&lt;br /&gt;
	// write vertex&lt;br /&gt;
	gl_Position = vec4(position, size);&lt;br /&gt;
	velocity_out = vec4(velocity, lifetime);&lt;br /&gt;
	EmitVertex();&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da wir GL_POINTS rendern ist ein Primitiv genau einen Vertex groß. Aus diesem Grund eignet sich die eingebaute Variable {{INLINE_CODE|gl_PrimitiveIDIn}} perfekt als Arrayindex. Via TexelFetch können wir also Velocity, Lebenszeit und aktuellen Seed auslesen.&lt;br /&gt;
&lt;br /&gt;
Ist die Lebenszeit eines Partikels abgelaufen, brechen wir die Verarbeitung dieses Partikels ab. Die Funktion {{INLINE_CODE|EmitVertex()}} wird also nicht aufgerufen und der Partikel somit unterdrückt. Der entscheidende Vorteil von Transform-Feedback an dieser Stelle ist nun, das beim schreiben in den Outputbuffer &amp;lt;u&amp;gt;keine&amp;lt;/u&amp;gt; Lücke entsteht. Es tritt also keine Fragmentierung auf. Am Ende des Updatevorgangs befinden sich alle Partikel aufgeräumt am Anfang des Buffers. Mit einem Query lässt sich auch die Anzahl der in den Outputbuffer geschriebenen Partikel auslesen, beim nächsten Frame wissen wir also exakt wie viele Partikel wir rendern müssen.&lt;br /&gt;
&lt;br /&gt;
Bezüglich des verwendeten Zufallsgenerators in der Funktion {{INLINE_CODE|noiseField(float)}} sollte man bei Bedarf das [[shader_Zufallsgenerator|Zufallsgenerator]]-Beispiel aus der [[Shadersammlung]] konsultieren. Der Rest des Update-Shaders sollte eigentlich klar sein.&lt;br /&gt;
&lt;br /&gt;
Via Transform-Feedback werden die Inhalte {{INLINE_CODE|gl_Position}} und den beiden varyings {{INLINE_CODE|velocity_out}} und {{INLINE_CODE|seed}} in ihre jeweiligen Outputbuffer geschrieben.&lt;br /&gt;
&lt;br /&gt;
=== Emitter ===&lt;br /&gt;
Nachdem wir nun den Update-Schritt erledigt haben, stellt sich die Frage wie den nun eigentlich neue Partikel ins System kommen sollen. Dank Transform-Feedback geht dies überraschend einfach. Wir brauchen keine Fragmentierung oder ähnliches beachten, sondern einfach nur die Daten der neuen Partikel ans Ende der jeweiligen Buffer schreiben.&lt;br /&gt;
&lt;br /&gt;
Wir könnten natürlich [[glBufferSubData]] benutzen, aber da wir nicht genau wissen wie viele Partikel gerade aktiv sind, wissen wir nicht in welchen Bereich geschrieben werden muss. Natürlich könnte man jetzt einen Query benutzen um die Anzahl der via Transform-Feedback geschriebenen Partikel zu ermitteln, aber da wir sowieso schon alles eingerichtet haben können wir die neuen Partikel auch einfach rendern.&lt;br /&gt;
&lt;br /&gt;
Während Transform-Feedback aktiv ist, ist es nicht erlaubt den Shader zu wechseln. Aus diesem Grund verwenden wir einfach weiter unseren Update-Shader, wobei wir die Uniform {{INLINE_CODE|timeElapsed}} auf 0 setzen. So werden zwar einige unnötige Berechnungen durchgeführt, aber normalerweise emittiert man sowieso nur einige 100 Partikel pro Frame. Wie die Partikel dann letztlich genau gerendert werden ist daher nur von geringer Bedeutung. Ich verwende hier zunächst mehrere {{INLINE_CODE|std::vector}} um die nach und nach von der Physik und Spiellogik erzeugten Partikel zu sammeln. Einmal pro Frame werden diese dann in Buffer-Objekte geschrieben.&lt;br /&gt;
&lt;br /&gt;
Sollte die maximale Größe der Partikelbuffer überschritten werden, werden die folgenden Partikel von Transform-Feedback stillschweigend unterdrückt. Um im nächsten Frame zu wissen wie viele Partikel aktiv sind, verwenden wir einen Query. Hier nun der Code zum aktualisieren der alten und emittieren der neuen Partikel.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;void CParticleSystem::update(double dTimeElapsed) {&lt;br /&gt;
    // flip buffers&lt;br /&gt;
    int input = m_flip;&lt;br /&gt;
    int output = m_flip = (1 - m_flip);&lt;br /&gt;
&lt;br /&gt;
    // setup shader&lt;br /&gt;
    glUseProgram(m_prgMove);&lt;br /&gt;
    glUniform1f(m_locMoveTimeElapsed, (float)dTimeElapsed);&lt;br /&gt;
&lt;br /&gt;
    // setup transform feedback&lt;br /&gt;
    glBindBufferBaseNV(GL_TRANSFORM_FEEDBACK_BUFFER_NV, 0, m_vboPosition[output]);&lt;br /&gt;
     glBindBufferBaseNV(GL_TRANSFORM_FEEDBACK_BUFFER_NV, 1, m_vboVelocity[output]);&lt;br /&gt;
     glBindBufferBaseNV(GL_TRANSFORM_FEEDBACK_BUFFER_NV, 2, m_vboRandom[output]);&lt;br /&gt;
    GLint locations[3] = { m_locMovePosition, m_locMoveVelocity, m_locMoveSeed };&lt;br /&gt;
    glTransformFeedbackVaryingsNV(m_prgMove, 3, locations, GL_SEPARATE_ATTRIBS_NV);&lt;br /&gt;
    glEnable(GL_RASTERIZER_DISCARD_NV);&lt;br /&gt;
    glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV, m_qryWritten);&lt;br /&gt;
    glBeginTransformFeedbackNV(GL_POINTS);&lt;br /&gt;
&lt;br /&gt;
    // draw active particles&lt;br /&gt;
    glActiveTexture(GL_TEXTURE1);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_BUFFER_EXT, m_tboRandom[input]);&lt;br /&gt;
    glActiveTexture(GL_TEXTURE0);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_BUFFER_EXT, m_tboVelocity[input]);&lt;br /&gt;
    glBindBuffer(GL_ARRAY_BUFFER, m_vboPosition[input]);&lt;br /&gt;
    glVertexPointer(4, GL_FLOAT, 0, 0);&lt;br /&gt;
    glEnableClientState(GL_VERTEX_ARRAY);    &lt;br /&gt;
    glDrawArrays(GL_POINTS, 0, m_active);&lt;br /&gt;
    &lt;br /&gt;
    // draw newly emitted particles&lt;br /&gt;
    if (m_emitted &amp;gt; 0) {&lt;br /&gt;
        glUniform1f(m_locMoveTimeElapsed, 0.0f);&lt;br /&gt;
        glActiveTexture(GL_TEXTURE1);&lt;br /&gt;
        glBindTexture(GL_TEXTURE_BUFFER_EXT, m_tboRandomEmit);&lt;br /&gt;
        glActiveTexture(GL_TEXTURE0);&lt;br /&gt;
        glBindTexture(GL_TEXTURE_BUFFER_EXT, m_tboVelocityEmit);&lt;br /&gt;
        glBindBuffer(GL_ARRAY_BUFFER, m_vboPositionEmit);&lt;br /&gt;
        glVertexPointer(4, GL_FLOAT, 0, 0);&lt;br /&gt;
        glDrawArrays(GL_POINTS, 0, m_emitted);&lt;br /&gt;
        m_emitted = 0;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // clean up transform feedback, retrieve count of processed particles&lt;br /&gt;
    glEndTransformFeedbackNV();&lt;br /&gt;
    glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV);&lt;br /&gt;
    glGetQueryObjectuiv(m_qryWritten, GL_QUERY_RESULT, &amp;amp;m_active);&lt;br /&gt;
&lt;br /&gt;
    // clean up&lt;br /&gt;
    glDisableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;
    glDisable(GL_RASTERIZER_DISCARD_NV);&lt;br /&gt;
    glBindBuffer(GL_ARRAY_BUFFER, 0);&lt;br /&gt;
    glUseProgram(0);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rendern ===&lt;br /&gt;
Ein Partikelsystem ist natürlich sinnfrei, wenn die Partikel nicht auch irgendwie dargestellt werden. Das ganze ist nicht weiter kompliziert. Wir benötigen weder die Velocity, die Lebenzeit noch den aktuellen Zufallsseed. Daher können wir einfach den Position-Buffer als Vertexbuffer rendern. Die Methode {{INLINE_CODE|render()}} und die verwendeten Shader sollten selbst erklärend sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;void CParticleSystem::render(CVector3f vEye) {&lt;br /&gt;
    int input = m_flip;&lt;br /&gt;
&lt;br /&gt;
    // setup shader&lt;br /&gt;
    CVector2i viewport = CViewerWidget::instance()-&amp;gt;getViewport();&lt;br /&gt;
    glUseProgram(m_prgRender);&lt;br /&gt;
    glUniform3f(m_locRenderEye, vEye.x, vEye.y, vEye.z);&lt;br /&gt;
    glUniform1f(m_locRenderViewport, viewport.y);&lt;br /&gt;
&lt;br /&gt;
    // setup vbo, tbo's, etc.&lt;br /&gt;
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);&lt;br /&gt;
    glEnable(GL_POINT_SPRITE);&lt;br /&gt;
    glTexEnvf(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); // generate TexCoords automatically&lt;br /&gt;
    glDisable(GL_ALPHA_TEST);&lt;br /&gt;
    glEnable(GL_BLEND);&lt;br /&gt;
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);&lt;br /&gt;
    glDepthMask(GL_FALSE);&lt;br /&gt;
    glActiveTexture(GL_TEXTURE0);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, m_texParticleAlpha);&lt;br /&gt;
    glBindBuffer(GL_ARRAY_BUFFER, m_vboPosition[input]);&lt;br /&gt;
    glVertexPointer(4, GL_FLOAT, 0, 0);&lt;br /&gt;
    glEnableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;
    &lt;br /&gt;
    // draw all active particles&lt;br /&gt;
    glDrawArrays(GL_POINTS, 0, m_active);&lt;br /&gt;
    &lt;br /&gt;
    // clean up    &lt;br /&gt;
    glDisableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;
    glDepthMask(GL_TRUE);&lt;br /&gt;
    glDisable(GL_BLEND);&lt;br /&gt;
    glDisable(GL_POINT_SPRITE);&lt;br /&gt;
    glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);&lt;br /&gt;
    glBindBuffer(GL_ARRAY_BUFFER, 0);&lt;br /&gt;
    glUseProgram(0);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Vertexshader''':&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform vec3 eye; // camera position&lt;br /&gt;
uniform float viewportHeight;&lt;br /&gt;
&lt;br /&gt;
varying float alphaScale;&lt;br /&gt;
&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
    vec4 position = vec4(gl_Vertex.xyz, 1.0);&lt;br /&gt;
    float size = gl_Vertex.w;&lt;br /&gt;
&lt;br /&gt;
    // transform position&lt;br /&gt;
    gl_Position = gl_ModelViewProjectionMatrix * position;&lt;br /&gt;
&lt;br /&gt;
    // calculate pointsize&lt;br /&gt;
    gl_PointSize = (viewportHeight * size) / distance(position.xyz, eye);&lt;br /&gt;
&lt;br /&gt;
    // rendering particles with size 0.0 may result in undefined behaivor&lt;br /&gt;
    gl_PointSize = clamp(gl_PointSize, 0.01, 64.0); &lt;br /&gt;
&lt;br /&gt;
    // derive an alpha value from particle size&lt;br /&gt;
    alphaScale = clamp(0.05 / size, 0.001, 0.2);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Fragmentshader''':&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D texAlpha;&lt;br /&gt;
&lt;br /&gt;
varying float alphaScale;&lt;br /&gt;
&lt;br /&gt;
const vec3 color = vec3(0.384, 0.343, 0.286);&lt;br /&gt;
&lt;br /&gt;
void main()&lt;br /&gt;
{	&lt;br /&gt;
    float alpha = texture2D(texAlpha, gl_TexCoord[0].xy).a * alphaScale;&lt;br /&gt;
    if (alpha &amp;lt; 0.0039) { discard; } // discard fragment when alpha smaller than 1/255&lt;br /&gt;
    gl_FragColor = vec4(color, alpha);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
= Erweiterungen =&lt;br /&gt;
Nun wollen wir das bestehende Partikelsystem erweitern. Es sollte klar sein das die hier beschriebenen Dinge sehr viel Rechenleistung benötigen. Diese Aktionen müssen also möglicherweise über mehrere Frames verteilt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Partikel/Partikel-Interaktion ==&lt;br /&gt;
Es gibt diverse Effekte bei denen man eine direkte Interaktion der Partikel untereinander benötigt. Beispiele sind wirkende Gravitationskräfte zwischen den Partikeln, die Simulation von Luftdruck und ähnliches. &lt;br /&gt;
&lt;br /&gt;
Da wir unsere Partikeldaten in Texturen (bzw. TBOs) gespeichert haben, ist eine solche Interaktion theoretisch leicht zu realisieren. Wir bauen im Update-Shader einfach eine Schleife über sämtliche anderen Partikel ein. Die [http://de.wikipedia.org/wiki/Gravitation#Newtonsches_Gravitationsgesetz Graviationskraft] berechnet sich nach der folgenden Formel. &lt;br /&gt;
:[[Bild:Partikel-Formel-Gravitation.gif]]&lt;br /&gt;
Dabei ist ''G'' die Graviationskonstante, ''M&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;'' bzw. ''M&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;'' die Masse und ''r'' der Abstand der beiden Partikel. Der Vektor ''e&amp;lt;sub&amp;gt;r&amp;lt;/sub&amp;gt;'' ist die normalisierte Richtung. [http://de.wikipedia.org/wiki/Beschleunigung Beschleunigung] ist Kraft durch Masse, d.h. die Masse des betrachteten Partikels kürzt sich aus der Gleichung.&lt;br /&gt;
:[[Bild:Partikel-Formel-Bescheunigung.gif]]&lt;br /&gt;
Im Beispiel werden einfach die Beschleunigungen zwischen den Partikeln berechnet und aufsummiert. Natürlich ziehen wir so viele Berechnungen wie möglich aus der Schleife hinaus.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;// particle interaction, naive approach&lt;br /&gt;
const float GRAVITATION = 6.67428e-11; // gravitational constant&lt;br /&gt;
vec3 accum = vec3(0,0,0);&lt;br /&gt;
for (int i=0; i&amp;lt;particleCount; ++i) {&lt;br /&gt;
    vec4 other = texelFetchBuffer(tboPosition, i); // other.xyz=Position, other.w=Mass&lt;br /&gt;
    vec3 dir = other.xyz - position;&lt;br /&gt;
    float len = length(dir);&lt;br /&gt;
    if (len &amp;gt; 0.0001) {&lt;br /&gt;
        accum += (other.w / len) * dir;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
velocity += GRAVITATION * timeElapsed * accum;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aufmerksame Leser wird nun hoffentlich verzweifelt den Kopf geschüttelt haben, den der Aufwand für diese Aktion ist natürlich quadratisch. Bei einem kleinen Partikelsystem mag dies machbar sein, aber bei einer Million Partikel würden wir insgesamt eine Billion Iterationen der inneren Schleife benötigen. Nun, Grafikkarten sind schnell, aber nicht schnell genug um dies in Echtzeit zu erledigen.&lt;br /&gt;
&lt;br /&gt;
Wir benötigten also eine andere Lösung. Es gibt prinzipiell drei verschiedene Ansätze, zumindest sind das die, die mir gerade einfallen.&lt;br /&gt;
# Verteilen der Operationen auf mehrere Frames. Dies macht nur bei mittelgroßen Partikelsystemen Sinn. Für ein doppelt so großes Partikelsystem muss man die Berechnung auf viermal so viele Frames verteilen um mit der gleichen Geschwindigkeit zu rendern. Das ist natürlich aussichtslos, da man immer noch quadratische Laufzeit hat.&lt;br /&gt;
# Verwendung einer Datenstruktur.  Üblicherweise nimmt der Einfluss der Partikel untereinander mit der Entfernung ab. Eigentlich muss man daher nur die Partikel in der näheren Umgebung berücksichtigen. Rekursive Datenstrukturen wie Bäume sind auf der Grafikkarte schwer zu realisieren. Aber man könnte die Eigenschaft ausnutzen das die Partikel sortiert sind. Logischerweise muss man dann die Partikel dann zuerst einmal sortieren, was natürlich ebenfalls sehr aufwendig ist. Aber falls man die Partikel aus einem anderen Grund zum Beispiel nach der Entfernung zur Kamera sortiert hat sollte man dies ausnutzen.&lt;br /&gt;
# Randomisierte Algorithmen zeichnen sich meist durch ihre Einfachheit und Effizienz aus. Einen solchen Ansatz wollen wir im folgenden näher betrachten. &lt;br /&gt;
&lt;br /&gt;
==== Randomisierung ====&lt;br /&gt;
[[Bild:Partikel-Gravitation.jpg|thumb|256px|Gravitationssimulation mit 262144 Partikeln und REP_COUNT=50 bei 39fps auf einer Nvidia GeForce 9800GT. Farben stehen für die Bewegungsrichtung.]]&lt;br /&gt;
Bei hundert tausenden Partikeln ist der Einfluss des einzelnen Partikels gering. Bei diesem Ansatz wählen wir jedes Frame eine neue zufällige Repräsentantenmenge von sagen wir 50 Partikeln. Das Ergebnis rechnen wir dann auf die gesamte Partikelwolke hoch. So erhalten wir eine Approximation der richtigen Lösung. Dies ist definitiv der schnellste Ansatz den die Laufzeit ist nur linear und die Qualität der Approximation lässt sich bequem über die Anzahl der Repräsentanten steuern.  &lt;br /&gt;
&lt;br /&gt;
Man kann die Repräsentantenmenge entweder für jeden Partikel einzeln im Shader generieren oder einmal für alle Partikel gleich als Uniform-Array (oder Textur) übergeben. Die zweite Variante hat den Vorteil, dass die Grafikkarte den Cache besser nutzen kann und man spart natürlich die Berechnung der Zufallszahlen im Shader. Dies sorgte in meinem Versuch für eine etwa 50% höhere Geschwindigkeit, wobei aber insgesamt weniger Partikel berücksichtigt werden und daher die Approximation auch etwas schlechter ist. Letztlich kommt es also ungefähr auf das gleiche hinaus.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel zeige ich hier einmal die erste Variante mit der auch der Screenshot rechts erzeugt wurde. Ich verwende hier wieder den Zufallsgenerator aus dem Artikel [[GLSL_noise|GLSL Noise]].&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;// particle interaction, randomization approach&lt;br /&gt;
const float GRAVITATION = 6.67428e-11; // gravitational constant&lt;br /&gt;
const int REP_COUNT = 50;              // number of representatives&lt;br /&gt;
vec3 accum = vec3(0,0,0);&lt;br /&gt;
for (int c=0; c&amp;lt;REP_COUNT; ++c) {&lt;br /&gt;
    seed = (seed * 1664525u + 1013904223u); &lt;br /&gt;
    int i = int(float(seed) / 4294967296.0 * particleCount);&lt;br /&gt;
    vec4 other = texelFetchBuffer(tboPosition, i);&lt;br /&gt;
    vec3 dir = other.xyz - position;&lt;br /&gt;
    float len = length(dir);&lt;br /&gt;
    if (len &amp;gt; 0.0001) {&lt;br /&gt;
        accum += (other.w / len) * dir;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
velocity += GRAVITATION * timeElapsed * (particleCount/float(REP_COUNT)) * accum;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Split und Merge ====&lt;br /&gt;
Da wir mit dem Geometryshader arbeiten, ist es problemlos möglich Split- oder Merge-Operationen auf die Partikel anzuwenden. Also beispielsweise könnten sich kollidierende Partikel vereinen. Ein Partikel bleibt dabei erhalten und vergrößert seine Masse auf die Summe beider Partikel. Die Bewegungsrichtung wird ebenfalls aus der Summe beider Partikel ermittelt. Der andere Partikel wird aus dem System entfernt, wie wir dies bereits vom Update-Shader im [[#Update|Grundlagen-Abschnitt]] kennen. Natürlich benötigt man irgendeine Konvention welcher der beiden Partikel erhalten bleibt und welcher entfernt wird, da man die Entscheidung natürlich für jeden der beiden Partikel einzeln treffen muss. Anbieten würde sich beispielsweise ein Vergleich der Position im Vertexbuffer ({{INLINE_CODE|gl_PrimitiveIDIn}}).&lt;br /&gt;
&lt;br /&gt;
Umgekehrt könnte ein Partikel in mehrere Teile zerfallen. Dies lässt sich leicht realisieren, der Geometryshader emittiert einfach entsprechend viele Partikel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Sortieren nach Z ==&lt;br /&gt;
Verwendet man zum Beispiel additives Alpha-Blending ist die Reihenfolge in der die Partikel gerendert werden nicht von belang. Arbeitet man dagegen mit einer nicht-assoziativen Blending-Operation, beispielsweise der folgenden, muss man die Partikel eigentlich nach der Entfernung zur Kamera sortieren um ein korrektes Ergebnis zu erzielen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Insbesondere wenn alle Partikel die gleiche Farbe und einen sehr geringen Alpha-Wert haben ist der Effekt des einzelnen Partikels nur sehr gering. In so einem Fall ist das Ergebnis ohne Sortierung zwar eigentlich falsch, aber man wird kaum einen Fehler entdecken. Auch hier kann man also auf die aufwendige Sortierung verzichten. Natürlich gibt es aber auch Fälle in denen man auf korrektes Rendering angewiesen ist. Das kann der Fall sein wenn man beispielsweise Feuer und Rauch in einem einzigen Partikelsystem verwaltet. Die Farben der Partikel unterscheiden sich stark. Nur mit einer Sortierung der Partikel lässt sich das korrekte Ergebnis erzielen.&lt;br /&gt;
&lt;br /&gt;
Sofern das Partikelsystem auf der CPU läuft ist die Sortierung kaum ein Problem. Die Anzahl der Partikel ist gering, geeignete Algorithmen (z.B. Quick-Sort, Intro-Sort, ...) sind gut erforscht und in jeder guten Standardbibliothek implementiert. Bei einem GPU-Partikelsystem steht man dagegen vor einem Problem. Zum einen verwaltet man üblicherweise wesentlich mehr Partikel, zum anderen arbeitet die GPU parallel und es gibt keinen Random-Write-Access, d.h. die Standard-Algorithmen lassen sich nicht sinnvoll anwenden.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es Algorithmen die es ermöglichen halbwegs effizient auf der GPU zu sortieren.&lt;br /&gt;
* ''Odd-Even Transition Sort'', ein naiver, nicht praktikabler Ansatz, Laufzeit: O(N&amp;amp;sup2; / P)&lt;br /&gt;
* ''Odd-Even Merge Sort'', Laufzeit: O((N log&amp;amp;sup2;N) / P)&lt;br /&gt;
* ''Bitonic Merge Sort'', Laufzeit: O((N log&amp;amp;sup2;N) / P)&lt;br /&gt;
* ''Adaptive Bitonic Sort'', Laufzeit: O((N log N) / P)&lt;br /&gt;
Bei den Angaben zur Laufzeit bezieht sich N auf die Anzahl der Partikel und P auf die Anzahl der parallel arbeitenden Shader-Einheiten.&lt;br /&gt;
&lt;br /&gt;
===Referenzen===&lt;br /&gt;
* P. Kipfer und R. Westermann, [http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter46.html GPU Gems 2, Chapter 46. Improved GPU Sorting], Technische Universität München, 2004&lt;br /&gt;
: Die Algorithmen ''Odd-Even Transition Sort'', ''Odd-Even Merge Sort'' sowie ''Bitonic Merge Sort'' werden hier erklärt.&lt;br /&gt;
* A. Greß und G. Zachmann,  [http://zach.in.tu-clausthal.de/papers/gpu-abisort-ipdps-2006.html GPU-ABiSort: Optimal Parallel Sorting on Stream Architectures], TU Clausthal, 2006&lt;br /&gt;
: Der Algorithmus ''Adaptive Bitonic Sort'' wir hier beschrieben.&lt;br /&gt;
* Bilardi, G. und Nicolau, [http://dspace.library.cornell.edu:8080/bitstream/1813/6609/2/86-769.ps Adaptive Bitonic Sorting: an Optimal Parallel Algorithm for Shared Memory Machines], Cornell University, 1986&lt;br /&gt;
: Brauchbarer mathematischer Beweis für die Aussagen über Bitonic-Sequenzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Transparente Schatten (mit Self-Shadows) ==&lt;br /&gt;
===Referenzen===&lt;br /&gt;
* T. Lokovic und E. Veach, [http://graphics.stanford.edu/papers/deepshadows/ Deep Shadow Maps], SIGGRAPH 2000 Proceedings, Addison-Wesley, August 2000&lt;br /&gt;
* C. Dachsbacher† und M. Stamminger, [http://www.vis.uni-stuttgart.de/~dachsbcn/download/tsm.pdf Translucent Shadow Maps], University of Erlangen-Nuremberg, 2003&lt;br /&gt;
&lt;br /&gt;
=Links=&lt;br /&gt;
* [[Shader]] : Shader Grundlagen&lt;br /&gt;
* [[Tutorial_glsl|Tutorial GLSL]] : Einführung in GLSL&lt;br /&gt;
* [[GLSL Partikel]] : GPU-Partikelsystem mit älterer Hardware&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Technik_oder_Algorithmus]]&lt;br /&gt;
[[Kategorie:Anleitung]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=GLSL_Partikel_2&amp;diff=24705</id>
		<title>GLSL Partikel 2</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=GLSL_Partikel_2&amp;diff=24705"/>
				<updated>2010-02-20T16:03:22Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Update */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Partikel-Hovercraft.jpg|thumb|256px|Hovercraft wirbelt Staub auf.]]&lt;br /&gt;
Der [[GLSL_Partikel|klassische Ansatz]] eines GPU Partikelsystems verwendet eine oder mehrere Texturen um die Partikel zu speichern. Zur Aktualisierung wird ein Quad in Größe dieser Textur gerendert. Der [[Fragmentshader]] liest die alten Daten aus einem Backbuffer und schreibt die aktualisierten Partikel in den Framebuffer. Dieses Verfahren ist an sich sehr effizient, hat aber einige Nachteile:&lt;br /&gt;
* Es ist sehr kompliziert effizient neue Partikel von der CPU zu emittieren. Die CPU muss immer wissen wo sich aktive Partikel in der Textur befinden. Sind die Lebensdauern der Partikel unterschiedlich (oder gar zufällig) wird dies zu einem echten Problem. In der Regel erfordert dies zumindest die Berechnung der Lebendauer zusätzlich auch auf der CPU durchzuführen, was natürlich nicht der Sinn eines GPU Partikelsystems ist.&lt;br /&gt;
* Da die Partikelanordnung wie gerade beschrieben fragmentiert, muss beim rendern der Partikel immer die vollständige Textur verarbeitet werden, auch wenn eigentlich nur wenige Partikel tatsächlich aktiv sind. Außerdem ist zum rendern ein großes [[VBO]] mit Dummy-Partikeln erforderlich, die eigentlich keine Daten enthalten.&lt;br /&gt;
* Sollen die Partikel zum Beispiel nach der Entfernung zur Kamera sortiert werden, ist dies sehr aufwendig. Auf der GPU verwendet man üblicherweise Odd-Even-Mergesort oder Bitonic-Sort. Beide Algorithmen vergleichen jeweils zwei Elemente und führen dann ggf. einen Tausch (Swap) dieser Elemente aus. Im Fragmentshader lässt sich jedoch die Position eines Fragments nicht mehr nachträglich ändern. Eine Implementierung im Fragmentshader erfordert also, dass jeder Vergleich zweimal durchgeführt wird. Das bedeutet auch, dass jeder Texturzugriff zweimal durchgeführt werden muss. Zudem erfordern beide Algorithmen sehr viele Passes. Für eine Million Partikel (1024²) sind so etwa 210 Passes erforderlich. Dies ist auch bei einer Verteilung über mehrere Frames nicht mehr sinnvoll in Echtzeit durchführbar.&lt;br /&gt;
Durch die Benutzung der Features aktueller Grafikhardware wie dem [[Geometryshader]], [[Transform-Feedback]], [[Instancing]] sowie diverser weiterer Features des Shader Model 4.0 lässt sich hier viel optimieren. Dieser Artikel setzt die Kenntnis dieser Funktionen voraus. Desweiteren sollte man natürlich wissen wie ein [[Partikelsystem|(state-preserving) Partikelsystem]] funktioniert, also zum Beispiel etwas mit den Begriffen [[Partikelsystem:Emitter|Emitter]] und [[Partikelsystem:Forcefield|Forcefield]] anfangen können.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
Ich verwende hier neben OpenGL 2.0 auch zahlreiche Extensions. Wenn man das Grundprinzip verstanden hat sollte es kein Problem sein diese Extensions durch eventuell verfügbare aktuellere Extensions zu ersetzen.&lt;br /&gt;
* [[GL_ARB_texture_float]]&lt;br /&gt;
* [[GL_EXT_texture_integer]]&lt;br /&gt;
* [[GL_EXT_texture_buffer_object]]&lt;br /&gt;
* [[GL_EXT_gpu_shader4]]&lt;br /&gt;
* [[GL_NV_transform_feedback]]&lt;br /&gt;
* [[GL_EXT_geometry_shader4]]&lt;br /&gt;
Zum speichern der Partikeldaten verwende ich mehrere Texturbuffer-Objects (TBO).&lt;br /&gt;
* ein 4x32bit-float Buffer zum speichern der Position und Größe des Partikels (''GL_RGBA32F_ARB'')&lt;br /&gt;
* ein weiterer 4x32bit-float Buffer zum speichern der Velocity und Lebenszeit des Partikels (''GL_RGBA32F_ARB'')&lt;br /&gt;
* ein 32bit-unsigned Buffer zum speichern des aktuellen Zufallsseed (''GL_LUMINANCE32UI_EXT'')&lt;br /&gt;
Alle Partikel werden durch Emitter erzeugt. Da uns später immer die genaue Anzahl der aktiven Partikel bekannt sein wird, müssen die Buffer nicht initialisiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
Das wichtigste an einem Partikelsystem ist immer der Update-Schritt. Daher fangen wir auch damit an. Auch wenn wir erst später erfahren wie neu erzeugte Partikel eigentlich in den Buffer kommen, nehmen wir zunächst einmal an die Buffer wären mit aktiven Partikeln gefüllt.&lt;br /&gt;
&lt;br /&gt;
Eine Grafikkarte kann nicht aus einem Buffer lesen und gleichzeitig auch hinein schreiben. Wir benötigen also jeden Buffer doppelt: Einen Inputbuffer aus dem wir als VBO bzw. TBO lesen, während wir in den Outputbuffer via Transform-Feedback schreiben. Nach jedem Updateschritt werden die Rollen der Buffer getauscht.&lt;br /&gt;
&lt;br /&gt;
Zunächst interpretieren wir den Position-Inputbuffer als Vertexbuffer und rendern sämtliche Partikel. Der Vertexshader ist trivial, denn die eigentliche Arbeit verrichtet der Geometryshader. Clipping und alles was danach in der [[:Bild:Pipeline.png|Pipeline]] kommt wird nicht benötigt. Also insbesondere der Rasterizer und der Fragmentshader. Darum schalten wird diesen unnötigen Kram mit {{INLINE_CODE|glEnable(GL_RASTERIZER_DISCARD_NV)}} einfach ab. Man sollte natürlich nicht vergessen das ganze später wieder einzuschalten.&lt;br /&gt;
&lt;br /&gt;
'''Vertexshader''':&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;void main() {&lt;br /&gt;
	gl_Position = gl_Vertex;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Geometryshader''':&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;#extension GL_EXT_gpu_shader4 : enable&lt;br /&gt;
#extension GL_EXT_geometry_shader4: enable&lt;br /&gt;
&lt;br /&gt;
uniform samplerBuffer tboVelocity;&lt;br /&gt;
uniform usamplerBuffer tboRandom;&lt;br /&gt;
uniform float timeElapsed;&lt;br /&gt;
&lt;br /&gt;
varying out vec4 velocity_out;&lt;br /&gt;
varying out unsigned int seed;&lt;br /&gt;
&lt;br /&gt;
const vec3 wind = vec3(1.0, 1.0, 2.0);&lt;br /&gt;
&lt;br /&gt;
vec3 constField() {&lt;br /&gt;
	return timeElapsed * wind;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
vec3 dampField(vec3 velocity, float strength) {&lt;br /&gt;
	return -velocity*strength*timeElapsed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
vec3 noiseField(float strength) {&lt;br /&gt;
	vec3 result;&lt;br /&gt;
	seed = (seed * 1103515245u + 12345u); result.x = float(seed);&lt;br /&gt;
	seed = (seed * 1103515245u + 12345u); result.y = float(seed);&lt;br /&gt;
	seed = (seed * 1103515245u + 12345u); result.z = float(seed);&lt;br /&gt;
	return timeElapsed * strength * ((result / 4294967296.0) - vec3(0.5,0.5,0.5));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	// get all particle data&lt;br /&gt;
	vec3 position = gl_PositionIn[0].xyz;&lt;br /&gt;
	float size = gl_PositionIn[0].w;&lt;br /&gt;
&lt;br /&gt;
	vec4 tmp = texelFetchBuffer(tboVelocity, gl_PrimitiveIDIn);&lt;br /&gt;
	vec3 velocity = tmp.xyz;&lt;br /&gt;
	float lifetime = tmp.w;&lt;br /&gt;
&lt;br /&gt;
	// update lifetime and discard dead particles&lt;br /&gt;
	lifetime -= timeElapsed;&lt;br /&gt;
	if (lifetime &amp;lt;= 0.0) { return; }&lt;br /&gt;
&lt;br /&gt;
	// update position and size&lt;br /&gt;
	position += velocity * timeElapsed;&lt;br /&gt;
	size += 0.3 * size * timeElapsed;&lt;br /&gt;
	&lt;br /&gt;
	// read current random seed&lt;br /&gt;
	seed = texelFetchBuffer(tboRandom, gl_PrimitiveIDIn).x;&lt;br /&gt;
&lt;br /&gt;
	// update velocity by applying force fields&lt;br /&gt;
	velocity += dampField(velocity, 0.5);&lt;br /&gt;
	velocity += constField();&lt;br /&gt;
	velocity += noiseField(1.0);&lt;br /&gt;
	&lt;br /&gt;
	// write vertex&lt;br /&gt;
	gl_Position = vec4(position, size);&lt;br /&gt;
	velocity_out = vec4(velocity, lifetime);&lt;br /&gt;
	EmitVertex();&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da wir GL_POINTS rendern ist ein Primitiv genau einen Vertex groß. Aus diesem Grund eignet sich die eingebaute Variable {{INLINE_CODE|gl_PrimitiveIDIn}} perfekt als Arrayindex. Via TexelFetch können wir also Velocity, Lebenszeit und aktuellen Seed auslesen.&lt;br /&gt;
&lt;br /&gt;
Ist die Lebenszeit eines Partikels abgelaufen, brechen wir die Verarbeitung dieses Partikels ab. Die Funktion {{INLINE_CODE|EmitVertex()}} wird also nicht aufgerufen und der Partikel somit unterdrückt. Der entscheidende Vorteil von Transform-Feedback an dieser Stelle ist nun, das beim schreiben in den Outputbuffer &amp;lt;u&amp;gt;keine&amp;lt;/u&amp;gt; Lücke entsteht. Es tritt also keine Fragmentierung auf. Am Ende des Updatevorgangs befinden sich alle Partikel aufgeräumt am Anfang des Buffers. Mit einem Query lässt sich auch die Anzahl der in den Outputbuffer geschriebenen Partikel auslesen, beim nächsten Frame wissen wir also exakt wie viele Partikel wir rendern müssen.&lt;br /&gt;
&lt;br /&gt;
Bezüglich des verwendeten Zufallsgenerators in der Funktion {{INLINE_CODE|noiseField(float)}} sollte man bei Bedarf das [[shader_Zufallsgenerator|Zufallsgenerator]]-Beispiel aus der [[Shadersammlung]] konsultieren. Der Rest des Update-Shaders sollte eigentlich klar sein.&lt;br /&gt;
&lt;br /&gt;
Via Transform-Feedback werden die Inhalte {{INLINE_CODE|gl_Position}} und den beiden varyings {{INLINE_CODE|velocity_out}} und {{INLINE_CODE|seed}} in ihre jeweiligen Outputbuffer geschrieben.&lt;br /&gt;
&lt;br /&gt;
=== Emitter ===&lt;br /&gt;
Nachdem wir nun den Update-Schritt erledigt haben, stellt sich die Frage wie den nun eigentlich neue Partikel ins System kommen sollen. Dank Transform-Feedback geht dies überraschend einfach. Wir brauchen keine Fragmentierung oder ähnliches beachten, sondern einfach nur die Daten der neuen Partikel ans Ende der jeweiligen Buffer schreiben.&lt;br /&gt;
&lt;br /&gt;
Wir könnten natürlich [[glBufferSubData]] benutzen, aber da nicht genau wissen wie viele Partikel gerade aktiv sind, wissen wir nicht in welchen Bereich geschrieben werden muss. Natürlich könnte man jetzt einen Query benutzen um die Anzahl der via Transform-Feedback geschriebenen Partikel zu ermitteln, aber da wir sowieso schon alles eingerichtet haben können wir die neuen Partikel auch einfach rendern.&lt;br /&gt;
&lt;br /&gt;
Während Transform-Feedback aktiv ist, ist es nicht erlaubt den Shader zu wechseln. Aus diesem Grund verwenden wir einfach weiter unseren Update-Shader, wobei wir die Uniform {{INLINE_CODE|timeElapsed}} auf 0 setzen. So werden zwar einige unnötige Berechnungen durchgeführt, aber normalerweise emittiert man sowieso nur einige 100 Partikel pro Frame. Wie die Partikel dann letztlich genau gerendert werden ist daher nur von geringer Bedeutung. Ich verwende hier zunächst mehrere {{INLINE_CODE|std::vector}} um die nach und nach von der Physik und Spiellogik erzeugten Partikel zu sammeln. Einmal pro Frame werden diese dann in Buffer-Objekte geschrieben.&lt;br /&gt;
&lt;br /&gt;
Sollte die maximale Größe der Partikelbuffer überschritten werden, werden die folgenden Partikel von Transform-Feedback stillschweigend unterdrückt. Um im nächsten Frame zu wissen wie viele Partikel aktiv sind, verwenden wir einen Query. Hier nun der Code zum aktualisieren der alten und emittieren der neuen Partikel.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;void CParticleSystem::update(double dTimeElapsed) {&lt;br /&gt;
    // flip buffers&lt;br /&gt;
    int input = m_flip;&lt;br /&gt;
    int output = m_flip = (1 - m_flip);&lt;br /&gt;
&lt;br /&gt;
    // setup shader&lt;br /&gt;
    glUseProgram(m_prgMove);&lt;br /&gt;
    glUniform1f(m_locMoveTimeElapsed, (float)dTimeElapsed);&lt;br /&gt;
&lt;br /&gt;
    // setup transform feedback&lt;br /&gt;
    glBindBufferBaseNV(GL_TRANSFORM_FEEDBACK_BUFFER_NV, 0, m_vboPosition[output]);&lt;br /&gt;
     glBindBufferBaseNV(GL_TRANSFORM_FEEDBACK_BUFFER_NV, 1, m_vboVelocity[output]);&lt;br /&gt;
     glBindBufferBaseNV(GL_TRANSFORM_FEEDBACK_BUFFER_NV, 2, m_vboRandom[output]);&lt;br /&gt;
    GLint locations[3] = { m_locMovePosition, m_locMoveVelocity, m_locMoveSeed };&lt;br /&gt;
    glTransformFeedbackVaryingsNV(m_prgMove, 3, locations, GL_SEPARATE_ATTRIBS_NV);&lt;br /&gt;
    glEnable(GL_RASTERIZER_DISCARD_NV);&lt;br /&gt;
    glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV, m_qryWritten);&lt;br /&gt;
    glBeginTransformFeedbackNV(GL_POINTS);&lt;br /&gt;
&lt;br /&gt;
    // draw active particles&lt;br /&gt;
    glActiveTexture(GL_TEXTURE1);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_BUFFER_EXT, m_tboRandom[input]);&lt;br /&gt;
    glActiveTexture(GL_TEXTURE0);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_BUFFER_EXT, m_tboVelocity[input]);&lt;br /&gt;
    glBindBuffer(GL_ARRAY_BUFFER, m_vboPosition[input]);&lt;br /&gt;
    glVertexPointer(4, GL_FLOAT, 0, 0);&lt;br /&gt;
    glEnableClientState(GL_VERTEX_ARRAY);    &lt;br /&gt;
    glDrawArrays(GL_POINTS, 0, m_active);&lt;br /&gt;
    &lt;br /&gt;
    // draw newly emitted particles&lt;br /&gt;
    if (m_emitted &amp;gt; 0) {&lt;br /&gt;
        glUniform1f(m_locMoveTimeElapsed, 0.0f);&lt;br /&gt;
        glActiveTexture(GL_TEXTURE1);&lt;br /&gt;
        glBindTexture(GL_TEXTURE_BUFFER_EXT, m_tboRandomEmit);&lt;br /&gt;
        glActiveTexture(GL_TEXTURE0);&lt;br /&gt;
        glBindTexture(GL_TEXTURE_BUFFER_EXT, m_tboVelocityEmit);&lt;br /&gt;
        glBindBuffer(GL_ARRAY_BUFFER, m_vboPositionEmit);&lt;br /&gt;
        glVertexPointer(4, GL_FLOAT, 0, 0);&lt;br /&gt;
        glDrawArrays(GL_POINTS, 0, m_emitted);&lt;br /&gt;
        m_emitted = 0;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // clean up transform feedback, retrieve count of processed particles&lt;br /&gt;
    glEndTransformFeedbackNV();&lt;br /&gt;
    glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV);&lt;br /&gt;
    glGetQueryObjectuiv(m_qryWritten, GL_QUERY_RESULT, &amp;amp;m_active);&lt;br /&gt;
&lt;br /&gt;
    // clean up&lt;br /&gt;
    glDisableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;
    glDisable(GL_RASTERIZER_DISCARD_NV);&lt;br /&gt;
    glBindBuffer(GL_ARRAY_BUFFER, 0);&lt;br /&gt;
    glUseProgram(0);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rendern ===&lt;br /&gt;
Ein Partikelsystem ist natürlich sinnfrei, wenn die Partikel nicht auch irgendwie dargestellt werden. Das ganze ist nicht weiter kompliziert. Wir benötigen weder die Velocity, die Lebenzeit noch den aktuellen Zufallsseed. Daher können wir einfach den Position-Buffer als Vertexbuffer rendern. Die Methode {{INLINE_CODE|render()}} und die verwendeten Shader sollten selbst erklärend sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;void CParticleSystem::render(CVector3f vEye) {&lt;br /&gt;
    int input = m_flip;&lt;br /&gt;
&lt;br /&gt;
    // setup shader&lt;br /&gt;
    CVector2i viewport = CViewerWidget::instance()-&amp;gt;getViewport();&lt;br /&gt;
    glUseProgram(m_prgRender);&lt;br /&gt;
    glUniform3f(m_locRenderEye, vEye.x, vEye.y, vEye.z);&lt;br /&gt;
    glUniform1f(m_locRenderViewport, viewport.y);&lt;br /&gt;
&lt;br /&gt;
    // setup vbo, tbo's, etc.&lt;br /&gt;
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);&lt;br /&gt;
    glEnable(GL_POINT_SPRITE);&lt;br /&gt;
    glTexEnvf(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); // generate TexCoords automatically&lt;br /&gt;
    glDisable(GL_ALPHA_TEST);&lt;br /&gt;
    glEnable(GL_BLEND);&lt;br /&gt;
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);&lt;br /&gt;
    glDepthMask(GL_FALSE);&lt;br /&gt;
    glActiveTexture(GL_TEXTURE0);&lt;br /&gt;
    glBindTexture(GL_TEXTURE_2D, m_texParticleAlpha);&lt;br /&gt;
    glBindBuffer(GL_ARRAY_BUFFER, m_vboPosition[input]);&lt;br /&gt;
    glVertexPointer(4, GL_FLOAT, 0, 0);&lt;br /&gt;
    glEnableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;
    &lt;br /&gt;
    // draw all active particles&lt;br /&gt;
    glDrawArrays(GL_POINTS, 0, m_active);&lt;br /&gt;
    &lt;br /&gt;
    // clean up    &lt;br /&gt;
    glDisableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;
    glDepthMask(GL_TRUE);&lt;br /&gt;
    glDisable(GL_BLEND);&lt;br /&gt;
    glDisable(GL_POINT_SPRITE);&lt;br /&gt;
    glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);&lt;br /&gt;
    glBindBuffer(GL_ARRAY_BUFFER, 0);&lt;br /&gt;
    glUseProgram(0);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Vertexshader''':&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform vec3 eye; // camera position&lt;br /&gt;
uniform float viewportHeight;&lt;br /&gt;
&lt;br /&gt;
varying float alphaScale;&lt;br /&gt;
&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
    vec4 position = vec4(gl_Vertex.xyz, 1.0);&lt;br /&gt;
    float size = gl_Vertex.w;&lt;br /&gt;
&lt;br /&gt;
    // transform position&lt;br /&gt;
    gl_Position = gl_ModelViewProjectionMatrix * position;&lt;br /&gt;
&lt;br /&gt;
    // calculate pointsize&lt;br /&gt;
    gl_PointSize = (viewportHeight * size) / distance(position.xyz, eye);&lt;br /&gt;
&lt;br /&gt;
    // rendering particles with size 0.0 may result in undefined behaivor&lt;br /&gt;
    gl_PointSize = clamp(gl_PointSize, 0.01, 64.0); &lt;br /&gt;
&lt;br /&gt;
    // derive an alpha value from particle size&lt;br /&gt;
    alphaScale = clamp(0.05 / size, 0.001, 0.2);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Fragmentshader''':&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D texAlpha;&lt;br /&gt;
&lt;br /&gt;
varying float alphaScale;&lt;br /&gt;
&lt;br /&gt;
const vec3 color = vec3(0.384, 0.343, 0.286);&lt;br /&gt;
&lt;br /&gt;
void main()&lt;br /&gt;
{	&lt;br /&gt;
    float alpha = texture2D(texAlpha, gl_TexCoord[0].xy).a * alphaScale;&lt;br /&gt;
    if (alpha &amp;lt; 0.0039) { discard; } // discard fragment when alpha smaller than 1/255&lt;br /&gt;
    gl_FragColor = vec4(color, alpha);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
= Erweiterungen =&lt;br /&gt;
Nun wollen wir das bestehende Partikelsystem erweitern. Es sollte klar sein das die hier beschriebenen Dinge sehr viel Rechenleistung benötigen. Diese Aktionen müssen also möglicherweise über mehrere Frames verteilt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Partikel/Partikel-Interaktion ==&lt;br /&gt;
Es gibt diverse Effekte bei denen man eine direkte Interaktion der Partikel untereinander benötigt. Beispiele sind wirkende Gravitationskräfte zwischen den Partikeln, die Simulation von Luftdruck und ähnliches. &lt;br /&gt;
&lt;br /&gt;
Da wir unsere Partikeldaten in Texturen (bzw. TBOs) gespeichert haben, ist eine solche Interaktion theoretisch leicht zu realisieren. Wir bauen im Update-Shader einfach eine Schleife über sämtliche anderen Partikel ein. Die [http://de.wikipedia.org/wiki/Gravitation#Newtonsches_Gravitationsgesetz Graviationskraft] berechnet sich nach der folgenden Formel. &lt;br /&gt;
:[[Bild:Partikel-Formel-Gravitation.gif]]&lt;br /&gt;
Dabei ist ''G'' die Graviationskonstante, ''M&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;'' bzw. ''M&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;'' die Masse und ''r'' der Abstand der beiden Partikel. Der Vektor ''e&amp;lt;sub&amp;gt;r&amp;lt;/sub&amp;gt;'' ist die normalisierte Richtung. [http://de.wikipedia.org/wiki/Beschleunigung Beschleunigung] ist Kraft durch Masse, d.h. die Masse des betrachteten Partikels kürzt sich aus der Gleichung.&lt;br /&gt;
:[[Bild:Partikel-Formel-Bescheunigung.gif]]&lt;br /&gt;
Im Beispiel werden einfach die Beschleunigungen zwischen den Partikeln berechnet und aufsummiert. Natürlich ziehen wir so viele Berechnungen wie möglich aus der Schleife hinaus.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;// particle interaction, naive approach&lt;br /&gt;
const float GRAVITATION = 6.67428e-11; // gravitational constant&lt;br /&gt;
vec3 accum = vec3(0,0,0);&lt;br /&gt;
for (int i=0; i&amp;lt;particleCount; ++i) {&lt;br /&gt;
    vec4 other = texelFetchBuffer(tboPosition, i); // other.xyz=Position, other.w=Mass&lt;br /&gt;
    vec3 dir = other.xyz - position;&lt;br /&gt;
    float len = length(dir);&lt;br /&gt;
    if (len &amp;gt; 0.0001) {&lt;br /&gt;
        accum += (other.w / len) * dir;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
velocity += GRAVITATION * timeElapsed * accum;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aufmerksame Leser wird nun hoffentlich verzweifelt den Kopf geschüttelt haben, den der Aufwand für diese Aktion ist natürlich quadratisch. Bei einem kleinen Partikelsystem mag dies machbar sein, aber bei einer Million Partikel würden wir insgesamt eine Billion Iterationen der inneren Schleife benötigen. Nun, Grafikkarten sind schnell, aber nicht schnell genug um dies in Echtzeit zu erledigen.&lt;br /&gt;
&lt;br /&gt;
Wir benötigten also eine andere Lösung. Es gibt prinzipiell drei verschiedene Ansätze, zumindest sind das die, die mir gerade einfallen.&lt;br /&gt;
# Verteilen der Operationen auf mehrere Frames. Dies macht nur bei mittelgroßen Partikelsystemen Sinn. Für ein doppelt so großes Partikelsystem muss man die Berechnung auf viermal so viele Frames verteilen um mit der gleichen Geschwindigkeit zu rendern. Das ist natürlich aussichtslos, da man immer noch quadratische Laufzeit hat.&lt;br /&gt;
# Verwendung einer Datenstruktur.  Üblicherweise nimmt der Einfluss der Partikel untereinander mit der Entfernung ab. Eigentlich muss man daher nur die Partikel in der näheren Umgebung berücksichtigen. Rekursive Datenstrukturen wie Bäume sind auf der Grafikkarte schwer zu realisieren. Aber man könnte die Eigenschaft ausnutzen das die Partikel sortiert sind. Logischerweise muss man dann die Partikel dann zuerst einmal sortieren, was natürlich ebenfalls sehr aufwendig ist. Aber falls man die Partikel aus einem anderen Grund zum Beispiel nach der Entfernung zur Kamera sortiert hat sollte man dies ausnutzen.&lt;br /&gt;
# Randomisierte Algorithmen zeichnen sich meist durch ihre Einfachheit und Effizienz aus. Einen solchen Ansatz wollen wir im folgenden näher betrachten. &lt;br /&gt;
&lt;br /&gt;
==== Randomisierung ====&lt;br /&gt;
[[Bild:Partikel-Gravitation.jpg|thumb|256px|Gravitationssimulation mit 262144 Partikeln und REP_COUNT=50 bei 39fps auf einer Nvidia GeForce 9800GT. Farben stehen für die Bewegungsrichtung.]]&lt;br /&gt;
Bei hundert tausenden Partikeln ist der Einfluss des einzelnen Partikels gering. Bei diesem Ansatz wählen wir jedes Frame eine neue zufällige Repräsentantenmenge von sagen wir 50 Partikeln. Das Ergebnis rechnen wir dann auf die gesamte Partikelwolke hoch. So erhalten wir eine Approximation der richtigen Lösung. Dies ist definitiv der schnellste Ansatz den die Laufzeit ist nur linear und die Qualität der Approximation lässt sich bequem über die Anzahl der Repräsentanten steuern.  &lt;br /&gt;
&lt;br /&gt;
Man kann die Repräsentantenmenge entweder für jeden Partikel einzeln im Shader generieren oder einmal für alle Partikel gleich als Uniform-Array (oder Textur) übergeben. Die zweite Variante hat den Vorteil, dass die Grafikkarte den Cache besser nutzen kann und man spart natürlich die Berechnung der Zufallszahlen im Shader. Dies sorgte in meinem Versuch für eine etwa 50% höhere Geschwindigkeit, wobei aber insgesamt weniger Partikel berücksichtigt werden und daher die Approximation auch etwas schlechter ist. Letztlich kommt es also ungefähr auf das gleiche hinaus.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel zeige ich hier einmal die erste Variante mit der auch der Screenshot rechts erzeugt wurde. Ich verwende hier wieder den Zufallsgenerator aus dem Artikel [[GLSL_noise|GLSL Noise]].&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;// particle interaction, randomization approach&lt;br /&gt;
const float GRAVITATION = 6.67428e-11; // gravitational constant&lt;br /&gt;
const int REP_COUNT = 50;              // number of representatives&lt;br /&gt;
vec3 accum = vec3(0,0,0);&lt;br /&gt;
for (int c=0; c&amp;lt;REP_COUNT; ++c) {&lt;br /&gt;
    seed = (seed * 1664525u + 1013904223u); &lt;br /&gt;
    int i = int(float(seed) / 4294967296.0 * particleCount);&lt;br /&gt;
    vec4 other = texelFetchBuffer(tboPosition, i);&lt;br /&gt;
    vec3 dir = other.xyz - position;&lt;br /&gt;
    float len = length(dir);&lt;br /&gt;
    if (len &amp;gt; 0.0001) {&lt;br /&gt;
        accum += (other.w / len) * dir;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
velocity += GRAVITATION * timeElapsed * (particleCount/float(REP_COUNT)) * accum;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Split und Merge ====&lt;br /&gt;
Da wir mit dem Geometryshader arbeiten, ist es problemlos möglich Split- oder Merge-Operationen auf die Partikel anzuwenden. Also beispielsweise könnten sich kollidierende Partikel vereinen. Ein Partikel bleibt dabei erhalten und vergrößert seine Masse auf die Summe beider Partikel. Die Bewegungsrichtung wird ebenfalls aus der Summe beider Partikel ermittelt. Der andere Partikel wird aus dem System entfernt, wie wir dies bereits vom Update-Shader im [[#Update|Grundlagen-Abschnitt]] kennen. Natürlich benötigt man irgendeine Konvention welcher der beiden Partikel erhalten bleibt und welcher entfernt wird, da man die Entscheidung natürlich für jeden der beiden Partikel einzeln treffen muss. Anbieten würde sich beispielsweise ein Vergleich der Position im Vertexbuffer ({{INLINE_CODE|gl_PrimitiveIDIn}}).&lt;br /&gt;
&lt;br /&gt;
Umgekehrt könnte ein Partikel in mehrere Teile zerfallen. Dies lässt sich leicht realisieren, der Geometryshader emittiert einfach entsprechend viele Partikel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Sortieren nach Z ==&lt;br /&gt;
Verwendet man zum Beispiel additives Alpha-Blending ist die Reihenfolge in der die Partikel gerendert werden nicht von belang. Arbeitet man dagegen mit einer nicht-assoziativen Blending-Operation, beispielsweise der folgenden, muss man die Partikel eigentlich nach der Entfernung zur Kamera sortieren um ein korrektes Ergebnis zu erzielen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Insbesondere wenn alle Partikel die gleiche Farbe und einen sehr geringen Alpha-Wert haben ist der Effekt des einzelnen Partikels nur sehr gering. In so einem Fall ist das Ergebnis ohne Sortierung zwar eigentlich falsch, aber man wird kaum einen Fehler entdecken. Auch hier kann man also auf die aufwendige Sortierung verzichten. Natürlich gibt es aber auch Fälle in denen man auf korrektes Rendering angewiesen ist. Das kann der Fall sein wenn man beispielsweise Feuer und Rauch in einem einzigen Partikelsystem verwaltet. Die Farben der Partikel unterscheiden sich stark. Nur mit einer Sortierung der Partikel lässt sich das korrekte Ergebnis erzielen.&lt;br /&gt;
&lt;br /&gt;
Sofern das Partikelsystem auf der CPU läuft ist die Sortierung kaum ein Problem. Die Anzahl der Partikel ist gering, geeignete Algorithmen (z.B. Quick-Sort, Intro-Sort, ...) sind gut erforscht und in jeder guten Standardbibliothek implementiert. Bei einem GPU-Partikelsystem steht man dagegen vor einem Problem. Zum einen verwaltet man üblicherweise wesentlich mehr Partikel, zum anderen arbeitet die GPU parallel und es gibt keinen Random-Write-Access, d.h. die Standard-Algorithmen lassen sich nicht sinnvoll anwenden.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es Algorithmen die es ermöglichen halbwegs effizient auf der GPU zu sortieren.&lt;br /&gt;
* ''Odd-Even Transition Sort'', ein naiver, nicht praktikabler Ansatz, Laufzeit: O(N&amp;amp;sup2; / P)&lt;br /&gt;
* ''Odd-Even Merge Sort'', Laufzeit: O((N log&amp;amp;sup2;N) / P)&lt;br /&gt;
* ''Bitonic Merge Sort'', Laufzeit: O((N log&amp;amp;sup2;N) / P)&lt;br /&gt;
* ''Adaptive Bitonic Sort'', Laufzeit: O((N log N) / P)&lt;br /&gt;
Bei den Angaben zur Laufzeit bezieht sich N auf die Anzahl der Partikel und P auf die Anzahl der parallel arbeitenden Shader-Einheiten.&lt;br /&gt;
&lt;br /&gt;
===Referenzen===&lt;br /&gt;
* P. Kipfer und R. Westermann, [http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter46.html GPU Gems 2, Chapter 46. Improved GPU Sorting], Technische Universität München, 2004&lt;br /&gt;
: Die Algorithmen ''Odd-Even Transition Sort'', ''Odd-Even Merge Sort'' sowie ''Bitonic Merge Sort'' werden hier erklärt.&lt;br /&gt;
* A. Greß und G. Zachmann,  [http://zach.in.tu-clausthal.de/papers/gpu-abisort-ipdps-2006.html GPU-ABiSort: Optimal Parallel Sorting on Stream Architectures], TU Clausthal, 2006&lt;br /&gt;
: Der Algorithmus ''Adaptive Bitonic Sort'' wir hier beschrieben.&lt;br /&gt;
* Bilardi, G. und Nicolau, [http://dspace.library.cornell.edu:8080/bitstream/1813/6609/2/86-769.ps Adaptive Bitonic Sorting: an Optimal Parallel Algorithm for Shared Memory Machines], Cornell University, 1986&lt;br /&gt;
: Brauchbarer mathematischer Beweis für die Aussagen über Bitonic-Sequenzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Transparente Schatten (mit Self-Shadows) ==&lt;br /&gt;
===Referenzen===&lt;br /&gt;
* T. Lokovic und E. Veach, [http://graphics.stanford.edu/papers/deepshadows/ Deep Shadow Maps], SIGGRAPH 2000 Proceedings, Addison-Wesley, August 2000&lt;br /&gt;
* C. Dachsbacher† und M. Stamminger, [http://www.vis.uni-stuttgart.de/~dachsbcn/download/tsm.pdf Translucent Shadow Maps], University of Erlangen-Nuremberg, 2003&lt;br /&gt;
&lt;br /&gt;
=Links=&lt;br /&gt;
* [[Shader]] : Shader Grundlagen&lt;br /&gt;
* [[Tutorial_glsl|Tutorial GLSL]] : Einführung in GLSL&lt;br /&gt;
* [[GLSL Partikel]] : GPU-Partikelsystem mit älterer Hardware&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Technik_oder_Algorithmus]]&lt;br /&gt;
[[Kategorie:Anleitung]]&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24678</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24678"/>
				<updated>2010-01-31T16:23:37Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Voraussetzungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
Möglicherweise lässt sich das beschriebene Verfahren auch auf andere einfache geometrische Objekte verallgemeinern. In Frage kommen etwa Planetenringe. Auf diese Möglichkeiten geht dieser Artikel jedoch nicht weiter ein.&lt;br /&gt;
&lt;br /&gt;
Der hier vorgestellte Shader geht von nur einer einzigen Lichtquelle aus. Der Ansatz lässt sich aber problemlos auf mehrere Lichtquellen erweitern. Allerdings steigt damit natürlich der Rechenaufwand.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir hier eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig um ein Zentrum angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen, wie in der Realität, weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt, wie zu erkennen ist, auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen des Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
Es folgt eine große Fallunterscheidung die ermittelt in welcher Zone (Licht/Halbschatten/Vollschatten) wir uns befinden. Das Ergebnis ist ein Faktor &amp;lt;tt&amp;gt;s&amp;lt;/tt&amp;gt; der später mit der Beleuchtung multipliziert wird. Dabei steht &amp;lt;tt&amp;gt;1.0&amp;lt;/tt&amp;gt; für volles Licht und &amp;lt;tt&amp;gt;0.0&amp;lt;/tt&amp;gt; für Dunkelheit. Zu beachten ist, dass &amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; auch negativ werden kann. In dem Fall befinden wir uns soweit hinter dem Occluder, dass keine Vollschatten-Zone mehr existiert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;if (dLF' &amp;lt; dLO || rF &amp;gt;= rF_outer) {&lt;br /&gt;
    // Fragment vor dem Occluder oder außerhalb des äußeren Kegels.&lt;br /&gt;
    s = 1.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
    // Wir befinden uns im Vollschatten&lt;br /&gt;
    s = 0.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= 0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
    // Wir befinden uns im Halbschatten, lineare Interpolation&lt;br /&gt;
    s = (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
    // Wir empfangen Licht von zwei Richtungen, also oben und unten am Occluder vorbei.&lt;br /&gt;
    // Die beiden rF heben sich gegenseitig auf.&lt;br /&gt;
    s = (-2*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Eine kleine Randnotiz: Der Schatten wird orthogonal zur Geraden &amp;lt;tt&amp;gt;LO&amp;lt;/tt&amp;gt; interpoliert. Diese Berechnung ist nicht physikalisch korrekt. Allerdings dürfte der Unterschied niemandem auffallen.}}&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Das &amp;quot;Drumherum&amp;quot;====&lt;br /&gt;
Im wesentlichen gibt es zwei Dinge zu beachten:&lt;br /&gt;
# Wann immer man geometrische Berechnungen durchführt muss man darauf achten das alle Punkte und Vektoren im gleichen Koordinatenraum vorliegen. Der hier vorgestellte Shader arbeitet im View-Space und erwartet daher auch die Daten der Lichtquelle und der Occluder-Objekte im View-Space.&amp;lt;br&amp;gt;Liegen also z.B. die Koordinaten der Lichtquelle in Weltkoordinaten vor, müssen sie vor Übergabe an den Shader mit der View-Matrix (das was z.B. [[gluLookAt]] produziert) multipliziert werden.&lt;br /&gt;
# Die Anzahl der Occluder die an den Shader übergeben werden sollte so klein wie möglich sein. Aus diesem Grund sollte man Objekte ausschließen die sowieso keine Auswirkungen auf das gerade gerenderte Objekt haben. Wir müssen also Objekte ausschließen die vollständig außerhalb des äußeren Kegels liegen. Ein derartiger Test funktioniert zumindest für Kugeln ähnlich wie der den wir im Shader für einzelne Punkte machen. Hier addieren/subtraieren wir aber noch an geeigneter Stelle den Radius des getesteten Objektes. Letztlich vergleichen wir die Länge von &amp;lt;tt&amp;gt;|P-P'| - rP&amp;lt;/tt&amp;gt; mit der Länge von &amp;lt;tt&amp;gt;rQ_outer&amp;lt;/tt&amp;gt;. Dieser Ansatz ist zwar nicht exakt, meldet aber niemals ein Objekt außerhalb, wenn es eigentlich innerhalb liegt. Umgekehrt kann dies allerdings passieren, da eigentlich der Punkt &amp;lt;tt&amp;gt;Q&amp;lt;/tt&amp;gt; geprüft wird.&lt;br /&gt;
&lt;br /&gt;
: [[Bild:shader_ConeVolumeExclude.png]]&lt;br /&gt;
&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24677</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24677"/>
				<updated>2010-01-31T16:15:12Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Funktionsweise */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
Möglicherweise lässt sich das beschriebene Verfahren auch auf andere einfache geometrische Objekte verallgemeinern. In Frage kommen etwa Planetenringe. Auf diese Möglichkeiten geht dieser Artikel jedoch nicht weiter ein.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir hier eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig um ein Zentrum angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen, wie in der Realität, weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt, wie zu erkennen ist, auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen des Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
Es folgt eine große Fallunterscheidung die ermittelt in welcher Zone (Licht/Halbschatten/Vollschatten) wir uns befinden. Das Ergebnis ist ein Faktor &amp;lt;tt&amp;gt;s&amp;lt;/tt&amp;gt; der später mit der Beleuchtung multipliziert wird. Dabei steht &amp;lt;tt&amp;gt;1.0&amp;lt;/tt&amp;gt; für volles Licht und &amp;lt;tt&amp;gt;0.0&amp;lt;/tt&amp;gt; für Dunkelheit. Zu beachten ist, dass &amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; auch negativ werden kann. In dem Fall befinden wir uns soweit hinter dem Occluder, dass keine Vollschatten-Zone mehr existiert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;if (dLF' &amp;lt; dLO || rF &amp;gt;= rF_outer) {&lt;br /&gt;
    // Fragment vor dem Occluder oder außerhalb des äußeren Kegels.&lt;br /&gt;
    s = 1.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
    // Wir befinden uns im Vollschatten&lt;br /&gt;
    s = 0.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= 0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
    // Wir befinden uns im Halbschatten, lineare Interpolation&lt;br /&gt;
    s = (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
    // Wir empfangen Licht von zwei Richtungen, also oben und unten am Occluder vorbei.&lt;br /&gt;
    // Die beiden rF heben sich gegenseitig auf.&lt;br /&gt;
    s = (-2*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Eine kleine Randnotiz: Der Schatten wird orthogonal zur Geraden &amp;lt;tt&amp;gt;LO&amp;lt;/tt&amp;gt; interpoliert. Diese Berechnung ist nicht physikalisch korrekt. Allerdings dürfte der Unterschied niemandem auffallen.}}&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Das &amp;quot;Drumherum&amp;quot;====&lt;br /&gt;
Im wesentlichen gibt es zwei Dinge zu beachten:&lt;br /&gt;
# Wann immer man geometrische Berechnungen durchführt muss man darauf achten das alle Punkte und Vektoren im gleichen Koordinatenraum vorliegen. Der hier vorgestellte Shader arbeitet im View-Space und erwartet daher auch die Daten der Lichtquelle und der Occluder-Objekte im View-Space.&amp;lt;br&amp;gt;Liegen also z.B. die Koordinaten der Lichtquelle in Weltkoordinaten vor, müssen sie vor Übergabe an den Shader mit der View-Matrix (das was z.B. [[gluLookAt]] produziert) multipliziert werden.&lt;br /&gt;
# Die Anzahl der Occluder die an den Shader übergeben werden sollte so klein wie möglich sein. Aus diesem Grund sollte man Objekte ausschließen die sowieso keine Auswirkungen auf das gerade gerenderte Objekt haben. Wir müssen also Objekte ausschließen die vollständig außerhalb des äußeren Kegels liegen. Ein derartiger Test funktioniert zumindest für Kugeln ähnlich wie der den wir im Shader für einzelne Punkte machen. Hier addieren/subtraieren wir aber noch an geeigneter Stelle den Radius des getesteten Objektes. Letztlich vergleichen wir die Länge von &amp;lt;tt&amp;gt;|P-P'| - rP&amp;lt;/tt&amp;gt; mit der Länge von &amp;lt;tt&amp;gt;rQ_outer&amp;lt;/tt&amp;gt;. Dieser Ansatz ist zwar nicht exakt, meldet aber niemals ein Objekt außerhalb, wenn es eigentlich innerhalb liegt. Umgekehrt kann dies allerdings passieren, da eigentlich der Punkt &amp;lt;tt&amp;gt;Q&amp;lt;/tt&amp;gt; geprüft wird.&lt;br /&gt;
&lt;br /&gt;
: [[Bild:shader_ConeVolumeExclude.png]]&lt;br /&gt;
&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24676</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24676"/>
				<updated>2010-01-31T16:13:15Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Funktionsweise */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
Möglicherweise lässt sich das beschriebene Verfahren auch auf andere einfache geometrische Objekte verallgemeinern. In Frage kommen etwa Planetenringe. Auf diese Möglichkeiten geht dieser Artikel jedoch nicht weiter ein.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir hier eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig um ein Zentrum angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen, wie in der Realität, weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt wie zu erkennen ist auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen der Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
Es folgt eine große Fallunterscheidung die ermittelt in welcher Zone (Licht/Halbschatten/Vollschatten) wir uns befinden. Das Ergebnis ist ein Faktor &amp;lt;tt&amp;gt;s&amp;lt;/tt&amp;gt; der später mit der Beleuchtung multipliziert wird. Dabei steht &amp;lt;tt&amp;gt;1.0&amp;lt;/tt&amp;gt; für volles Licht und &amp;lt;tt&amp;gt;0.0&amp;lt;/tt&amp;gt; für Dunkelheit. Zu beachten ist, dass &amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; auch negativ werden kann. In dem Fall befinden wir uns soweit hinter dem Occluder, dass keine Vollschatten-Zone mehr existiert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;if (dLF' &amp;lt; dLO || rF &amp;gt;= rF_outer) {&lt;br /&gt;
    // Fragment vor dem Occluder oder außerhalb des äußeren Kegels.&lt;br /&gt;
    s = 1.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
    // Wir befinden uns im Vollschatten&lt;br /&gt;
    s = 0.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= 0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
    // Wir befinden uns im Halbschatten, lineare Interpolation&lt;br /&gt;
    s = (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
    // Wir empfangen Licht von zwei Richtungen, also oben und unten am Occluder vorbei.&lt;br /&gt;
    // Die beiden rF heben sich gegenseitig auf.&lt;br /&gt;
    s = (-2*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Eine kleine Randnotiz: Der Schatten wird orthogonal zur Geraden &amp;lt;tt&amp;gt;LO&amp;lt;/tt&amp;gt; interpoliert. Diese Berechnung ist nicht physikalisch korrekt. Allerdings dürfte der Unterschied niemandem auffallen.}}&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Das &amp;quot;Drumherum&amp;quot;====&lt;br /&gt;
Im wesentlichen gibt es zwei Dinge zu beachten:&lt;br /&gt;
# Wann immer man geometrische Berechnungen durchführt muss man darauf achten das alle Punkte und Vektoren im gleichen Koordinatenraum vorliegen. Der hier vorgestellte Shader arbeitet im View-Space und erwartet daher auch die Daten der Lichtquelle und der Occluder-Objekte im View-Space.&amp;lt;br&amp;gt;Liegen also z.B. die Koordinaten der Lichtquelle in Weltkoordinaten vor, müssen sie vor Übergabe an den Shader mit der View-Matrix (das was z.B. [[gluLookAt]] produziert) multipliziert werden.&lt;br /&gt;
# Die Anzahl der Occluder die an den Shader übergeben werden sollte so klein wie möglich sein. Aus diesem Grund sollte man Objekte ausschließen die sowieso keine Auswirkungen auf das gerade gerenderte Objekt haben. Wir müssen also Objekte ausschließen die vollständig außerhalb des äußeren Kegels liegen. Ein derartiger Test funktioniert zumindest für Kugeln ähnlich wie der den wir im Shader für einzelne Punkte machen. Hier addieren/subtraieren wir aber noch an geeigneter Stelle den Radius des getesteten Objektes. Letztlich vergleichen wir die Länge von &amp;lt;tt&amp;gt;|P-P'| - rP&amp;lt;/tt&amp;gt; mit der Länge von &amp;lt;tt&amp;gt;rQ_outer&amp;lt;/tt&amp;gt;. Dieser Ansatz ist zwar nicht exakt, meldet aber niemals ein Objekt außerhalb, wenn es eigentlich innerhalb liegt. Umgekehrt kann dies allerdings passieren, da eigentlich der Punkt &amp;lt;tt&amp;gt;Q&amp;lt;/tt&amp;gt; geprüft wird.&lt;br /&gt;
&lt;br /&gt;
: [[Bild:shader_ConeVolumeExclude.png]]&lt;br /&gt;
&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24675</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24675"/>
				<updated>2010-01-31T15:29:30Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: Offline-Bearbeitung entfernt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
Möglicherweise lässt sich das beschriebene Verfahren auch auf andere einfache geometrische Objekte verallgemeinern. In Frage kommen etwa Planetenringe. Auf diese Möglichkeiten geht dieser Artikel jedoch nicht weiter ein.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen wie in der Realität weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt wie zu erkennen ist auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen der Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
Es folgt eine große Fallunterscheidung die ermittelt in welcher Zone (Licht/Halbschatten/Vollschatten) wir uns befinden. Das Ergebnis ist ein Faktor &amp;lt;tt&amp;gt;s&amp;lt;/tt&amp;gt; der später mit der Beleuchtung multipliziert wird. Dabei steht &amp;lt;tt&amp;gt;1.0&amp;lt;/tt&amp;gt; für volles Licht und &amp;lt;tt&amp;gt;0.0&amp;lt;/tt&amp;gt; für Dunkelheit. Zu beachten ist, dass &amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; auch negativ werden kann. In dem Fall befinden wir uns soweit hinter dem Occluder, dass keine Vollschatten-Zone mehr existiert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;if (dLF' &amp;lt; dLO || rF &amp;gt;= rF_outer) {&lt;br /&gt;
    // Fragment vor dem Occluder oder außerhalb des äußeren Kegels.&lt;br /&gt;
    s = 1.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
    // Wir befinden uns im Vollschatten&lt;br /&gt;
    s = 0.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= 0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
    // Wir befinden uns im Halbschatten, lineare Interpolation&lt;br /&gt;
    s = (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
    // Wir empfangen Licht von zwei Richtungen, also oben und unten am Occluder vorbei.&lt;br /&gt;
    // Die beiden rF heben sich gegenseitig auf.&lt;br /&gt;
    s = (-2*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Eine kleine Randnotiz: Der Schatten wird orthogonal zur Geraden &amp;lt;tt&amp;gt;LO&amp;lt;/tt&amp;gt; interpoliert. Diese Berechnung ist nicht physikalisch korrekt. Allerdings dürfte der Unterschied niemandem auffallen.}}&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Das &amp;quot;Drumherum&amp;quot;====&lt;br /&gt;
Im wesentlichen gibt es zwei Dinge zu beachten:&lt;br /&gt;
# Wann immer man geometrische Berechnungen durchführt muss man darauf achten das alle Punkte und Vektoren im gleichen Koordinatenraum vorliegen. Der hier vorgestellte Shader arbeitet im View-Space und erwartet daher auch die Daten der Lichtquelle und der Occluder-Objekte im View-Space.&amp;lt;br&amp;gt;Liegen also z.B. die Koordinaten der Lichtquelle in Weltkoordinaten vor, müssen sie vor Übergabe an den Shader mit der View-Matrix (das was z.B. [[gluLookAt]] produziert) multipliziert werden.&lt;br /&gt;
# Die Anzahl der Occluder die an den Shader übergeben werden sollte so klein wie möglich sein. Aus diesem Grund sollte man Objekte ausschließen die sowieso keine Auswirkungen auf das gerade gerenderte Objekt haben. Wir müssen also Objekte ausschließen die vollständig außerhalb des äußeren Kegels liegen. Ein derartiger Test funktioniert zumindest für Kugeln ähnlich wie der den wir im Shader für einzelne Punkte machen. Hier addieren/subtraieren wir aber noch an geeigneter Stelle den Radius des getesteten Objektes. Letztlich vergleichen wir die Länge von &amp;lt;tt&amp;gt;|P-P'| - rP&amp;lt;/tt&amp;gt; mit der Länge von &amp;lt;tt&amp;gt;rQ_outer&amp;lt;/tt&amp;gt;. Dieser Ansatz ist zwar nicht exakt, meldet aber niemals ein Objekt außerhalb, wenn es eigentlich innerhalb liegt. Umgekehrt kann dies allerdings passieren, da eigentlich der Punkt &amp;lt;tt&amp;gt;Q&amp;lt;/tt&amp;gt; geprüft wird.&lt;br /&gt;
&lt;br /&gt;
: [[Bild:shader_ConeVolumeExclude.png]]&lt;br /&gt;
&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24674</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24674"/>
				<updated>2010-01-31T15:26:51Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Das &amp;quot;Drumherum&amp;quot; */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
Möglicherweise lässt sich das beschriebene Verfahren auch auf andere einfache geometrische Objekte verallgemeinern. In Frage kommen etwa Planetenringe. Auf diese Möglichkeiten geht dieser Artikel jedoch nicht weiter ein.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen wie in der Realität weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt wie zu erkennen ist auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen der Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
Es folgt eine große Fallunterscheidung die ermittelt in welcher Zone (Licht/Halbschatten/Vollschatten) wir uns befinden. Das Ergebnis ist ein Faktor &amp;lt;tt&amp;gt;s&amp;lt;/tt&amp;gt; der später mit der Beleuchtung multipliziert wird. Dabei steht &amp;lt;tt&amp;gt;1.0&amp;lt;/tt&amp;gt; für volles Licht und &amp;lt;tt&amp;gt;0.0&amp;lt;/tt&amp;gt; für Dunkelheit. Zu beachten ist, dass &amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; auch negativ werden kann. In dem Fall befinden wir uns soweit hinter dem Occluder, dass keine Vollschatten-Zone mehr existiert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;if (dLF' &amp;lt; dLO || rF &amp;gt;= rF_outer) {&lt;br /&gt;
    // Fragment vor dem Occluder oder außerhalb des äußeren Kegels.&lt;br /&gt;
    s = 1.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
    // Wir befinden uns im Vollschatten&lt;br /&gt;
    s = 0.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= 0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
    // Wir befinden uns im Halbschatten, lineare Interpolation&lt;br /&gt;
    s = (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
    // Wir empfangen Licht von zwei Richtungen, also oben und unten am Occluder vorbei.&lt;br /&gt;
    // Die beiden rF heben sich gegenseitig auf.&lt;br /&gt;
    s = (-2*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Eine kleine Randnotiz: Der Schatten wird orthogonal zur Geraden &amp;lt;tt&amp;gt;LO&amp;lt;/tt&amp;gt; interpoliert. Diese Berechnung ist nicht physikalisch korrekt. Allerdings dürfte der Unterschied niemandem auffallen.}}&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Das &amp;quot;Drumherum&amp;quot;====&lt;br /&gt;
Im wesentlichen gibt es zwei Dinge zu beachten:&lt;br /&gt;
# Wann immer man geometrische Berechnungen durchführt muss man darauf achten das alle Punkte und Vektoren im gleichen Koordinatenraum vorliegen. Der hier vorgestellte Shader arbeitet im View-Space und erwartet daher auch die Daten der Lichtquelle und der Occluder-Objekte im View-Space.&amp;lt;br&amp;gt;Liegen also z.B. die Koordinaten der Lichtquelle in Weltkoordinaten vor, müssen sie vor Übergabe an den Shader mit der View-Matrix (das was z.B. [[gluLookAt]] produziert) multipliziert werden.&lt;br /&gt;
# Die Anzahl der Occluder die an den Shader übergeben werden sollte so klein wie möglich sein. Aus diesem Grund sollte man Objekte ausschließen die sowieso keine Auswirkungen auf das gerade gerenderte Objekt haben. Wir müssen also Objekte ausschließen die vollständig außerhalb des äußeren Kegels liegen. Ein derartiger Test funktioniert zumindest für Kugeln ähnlich wie der den wir im Shader für einzelne Punkte machen. Hier addieren/subtraieren wir aber noch an geeigneter Stelle den Radius des getesteten Objektes. Letztlich vergleichen wir die Länge von &amp;lt;tt&amp;gt;|P-P'| - rP&amp;lt;/tt&amp;gt; mit der Länge von &amp;lt;tt&amp;gt;rQ_outer&amp;lt;/tt&amp;gt;. Dieser Ansatz ist zwar nicht exakt, meldet aber niemals ein Objekt außerhalb, wenn es eigentlich innerhalb liegt. Umgekehrt kann dies allerdings passieren, da eigentlich der Punkt &amp;lt;tt&amp;gt;Q&amp;lt;/tt&amp;gt; geprüft wird.&lt;br /&gt;
&lt;br /&gt;
: [[Bild:shader_ConeVolumeExclude.png]]&lt;br /&gt;
&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24673</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24673"/>
				<updated>2010-01-31T15:24:31Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
Möglicherweise lässt sich das beschriebene Verfahren auch auf andere einfache geometrische Objekte verallgemeinern. In Frage kommen etwa Planetenringe. Auf diese Möglichkeiten geht dieser Artikel jedoch nicht weiter ein.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen wie in der Realität weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt wie zu erkennen ist auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen der Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
Es folgt eine große Fallunterscheidung die ermittelt in welcher Zone (Licht/Halbschatten/Vollschatten) wir uns befinden. Das Ergebnis ist ein Faktor &amp;lt;tt&amp;gt;s&amp;lt;/tt&amp;gt; der später mit der Beleuchtung multipliziert wird. Dabei steht &amp;lt;tt&amp;gt;1.0&amp;lt;/tt&amp;gt; für volles Licht und &amp;lt;tt&amp;gt;0.0&amp;lt;/tt&amp;gt; für Dunkelheit. Zu beachten ist, dass &amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; auch negativ werden kann. In dem Fall befinden wir uns soweit hinter dem Occluder, dass keine Vollschatten-Zone mehr existiert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;if (dLF' &amp;lt; dLO || rF &amp;gt;= rF_outer) {&lt;br /&gt;
    // Fragment vor dem Occluder oder außerhalb des äußeren Kegels.&lt;br /&gt;
    s = 1.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
    // Wir befinden uns im Vollschatten&lt;br /&gt;
    s = 0.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= 0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
    // Wir befinden uns im Halbschatten, lineare Interpolation&lt;br /&gt;
    s = (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
    // Wir empfangen Licht von zwei Richtungen, also oben und unten am Occluder vorbei.&lt;br /&gt;
    // Die beiden rF heben sich gegenseitig auf.&lt;br /&gt;
    s = (-2*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Eine kleine Randnotiz: Der Schatten wird orthogonal zur Geraden &amp;lt;tt&amp;gt;LO&amp;lt;/tt&amp;gt; interpoliert. Diese Berechnung ist nicht physikalisch korrekt. Allerdings dürfte der Unterschied niemandem auffallen.}}&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Das &amp;quot;Drumherum&amp;quot;====&lt;br /&gt;
Im wesentlichen gibt es zwei Dinge zu beachten:&lt;br /&gt;
# Wann immer man geometrische Berechnungen durchführt muss man darauf achten das alle Punkte und Vektoren im gleichen Koordinatenraum vorliegen. Der hier vorgestellte Shader arbeitet im View-Space und erwartet daher auch die Daten der Lichtquelle und der Occluder-objekt im View-Space.&amp;lt;br&amp;gt;Liegen also z.B. die Koordinaten der Lichtquelle in Weltkoordinaten vor, müssen sie vor Übergabe an den Shader mit der View-Matrix (das was z.B. [[gluLookAt]] produziert) multipliziert werden.&lt;br /&gt;
# Die Anzahl der Occluder die an den Shader übergeben werden sollte so klein wie möglich sein. Aus diesem Grund sollte man Objekte ausschließen die sowieso keine Auswirkungen auf das gerade gerenderte Objekt haben. Wir müssen also Objekte ausschließen die vollständig außerhalb des äußeren Kegels liegen. Ein derartiger Test funktioniert zumindest für Kugeln ähnlich wie der den wir im Shader für einzelne Punkte machen. Hier addieren/subtraieren wir aber noch an geeigneter Stelle den Radius des getesteten Objektes. Letztlich vergleichen wir die Länge von &amp;lt;tt&amp;gt;|P-P'| - rP&amp;lt;/tt&amp;gt; mit der Länge von &amp;lt;tt&amp;gt;rQ_outer&amp;lt;/tt&amp;gt;. Dieser Ansatz ist zwar nicht exakt, meldet aber niemals ein Objekt außerhalb, wenn es eigentlich innerhalb liegt. Umgekehrt kann dies allerdings passieren, da eigentlich der Punkt &amp;lt;tt&amp;gt;Q&amp;lt;/tt&amp;gt; geprüft wird.&lt;br /&gt;
&lt;br /&gt;
: [[Bild:shader_ConeVolumeExclude.png]]&lt;br /&gt;
&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:shader_ConeVolumeExclude.png&amp;diff=24672</id>
		<title>Datei:shader ConeVolumeExclude.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:shader_ConeVolumeExclude.png&amp;diff=24672"/>
				<updated>2010-01-31T15:18:04Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24671</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24671"/>
				<updated>2010-01-31T13:32:08Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Voraussetzungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
Möglicherweise lässt sich das beschriebene Verfahren auch auf andere einfache geometrische Objekte verallgemeinern. In Frage kommen etwa Planetenringe. Auf diese Möglichkeiten geht dieser Artikel jedoch nicht weiter ein.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen wie in der Realität weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt wie zu erkennen ist auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen der Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
Es folgt eine große Fallunterscheidung die ermittelt in welcher Zone (Licht/Halbschatten/Vollschatten) wir uns befinden. Das Ergebnis ist ein Faktor &amp;lt;tt&amp;gt;s&amp;lt;/tt&amp;gt; der später mit der Beleuchtung multipliziert wird. Dabei steht &amp;lt;tt&amp;gt;1.0&amp;lt;/tt&amp;gt; für volles Licht und &amp;lt;tt&amp;gt;0.0&amp;lt;/tt&amp;gt; für Dunkelheit. Zu beachten ist, dass &amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; auch negativ werden kann. In dem Fall befinden wir uns soweit hinter dem Occluder, dass keine Vollschatten-Zone mehr existiert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;if (dLF' &amp;lt; dLO || rF &amp;gt;= rF_outer) {&lt;br /&gt;
    // Fragment vor dem Occluder oder außerhalb des äußeren Kegels.&lt;br /&gt;
    s = 1.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
    // Wir befinden uns im Vollschatten&lt;br /&gt;
    s = 0.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= 0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
    // Wir befinden uns im Halbschatten, lineare Interpolation&lt;br /&gt;
    s = (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
    // Wir empfangen Licht von zwei Richtungen, also oben und unten am Occluder vorbei.&lt;br /&gt;
    // Die beiden rF heben sich gegenseitig auf.&lt;br /&gt;
    s = (-2*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Eine kleine Randnotiz: Der Schatten wird orthogonal zur Geraden &amp;lt;tt&amp;gt;LO&amp;lt;/tt&amp;gt; interpoliert. Diese Berechnung ist nicht physikalisch korrekt. Allerdings dürfte der Unterschied niemandem auffallen.}}&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24670</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24670"/>
				<updated>2010-01-31T13:08:25Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Funktionsweise */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen wie in der Realität weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt wie zu erkennen ist auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen der Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
Es folgt eine große Fallunterscheidung die ermittelt in welcher Zone (Licht/Halbschatten/Vollschatten) wir uns befinden. Das Ergebnis ist ein Faktor &amp;lt;tt&amp;gt;s&amp;lt;/tt&amp;gt; der später mit der Beleuchtung multipliziert wird. Dabei steht &amp;lt;tt&amp;gt;1.0&amp;lt;/tt&amp;gt; für volles Licht und &amp;lt;tt&amp;gt;0.0&amp;lt;/tt&amp;gt; für Dunkelheit. Zu beachten ist, dass &amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; auch negativ werden kann. In dem Fall befinden wir uns soweit hinter dem Occluder, dass keine Vollschatten-Zone mehr existiert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;if (dLF' &amp;lt; dLO || rF &amp;gt;= rF_outer) {&lt;br /&gt;
    // Fragment vor dem Occluder oder außerhalb des äußeren Kegels.&lt;br /&gt;
    s = 1.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
    // Wir befinden uns im Vollschatten&lt;br /&gt;
    s = 0.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= 0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
    // Wir befinden uns im Halbschatten, lineare Interpolation&lt;br /&gt;
    s = (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
    // Wir empfangen Licht von zwei Richtungen, also oben und unten am Occluder vorbei.&lt;br /&gt;
    // Die beiden rF heben sich gegenseitig auf.&lt;br /&gt;
    s = (-2*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Eine kleine Randnotiz: Der Schatten wird orthogonal zur Geraden &amp;lt;tt&amp;gt;LO&amp;lt;/tt&amp;gt; interpoliert. Diese Berechnung ist nicht physikalisch korrekt. Allerdings dürfte der Unterschied niemandem auffallen.}}&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24669</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24669"/>
				<updated>2010-01-31T13:05:52Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Funktionsweise */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen wie in der Realität weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt wie zu erkennen ist auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen der Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
Es folgt eine große Fallunterscheidung die ermittelt in welcher Zone (Licht/Halbschatten/Vollschatten) wir uns befinden. Das Ergebnis ist ein Faktor &amp;lt;tt&amp;gt;s&amp;lt;/tt&amp;gt; der später mit der Beleuchtung multipliziert wird. Dabei steht &amp;lt;tt&amp;gt;1.0&amp;lt;/tt&amp;gt; für volles Licht und &amp;lt;tt&amp;gt;0.0&amp;lt;/tt&amp;gt; für Dunkelheit. Zu beachten ist, dass &amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; auch negativ werden kann. In dem Fall befinden wir uns soweit hinter dem Occluder, dass keine Vollschatten-Zone mehr existiert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;if (dLF' &amp;lt; dLO || rF &amp;gt;= rF_outer) {&lt;br /&gt;
    // Fragment vor dem Occluder oder außerhalb des äußeren Kegels.&lt;br /&gt;
    s = 1.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
    // Wir befinden uns im Vollschatten&lt;br /&gt;
    s = 0.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= 0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
    // Wir befinden uns im Halbschatten, lineare Interpolation&lt;br /&gt;
    s = (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
    // Wir empfangen Licht von zwei Richtungen, also oben und unten am Occluder vorbei.&lt;br /&gt;
    // Die beiden rF heben sich gegenseitig auf.&lt;br /&gt;
    s = (-2*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24668</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24668"/>
				<updated>2010-01-31T13:04:26Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Funktionsweise */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen wie in der Realität weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt wie zu erkennen ist auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen der Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
Es folgt eine große Fallunterscheidung die ermittelt in welcher Zone (Licht/Halbschatten/Vollschatten) wir uns befinden. Das Ergebnis ist ein Faktor &amp;lt;tt&amp;gt;s&amp;lt;/tt&amp;gt; der später mit der Beleuchtung multipliziert wird. Dabei steht &amp;lt;tt&amp;gt;1.0&amp;lt;/tt&amp;gt; für volles Licht und &amp;lt;tt&amp;gt;0.0&amp;lt;/tt&amp;gt; für Dunkelheit. Zu beachten ist das im &amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; auch negativ werden kann. In dem Fall befinden wir uns soweit hinter dem Occluder, dass keine Vollschatten-Zone mehr existiert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;if (dLF' &amp;lt; dLO || rF &amp;gt;= rF_outer) {&lt;br /&gt;
    // Fragment vor dem Occluder oder außerhalb des äußeren Kegels.&lt;br /&gt;
    s = 1.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
    // Wir befinden uns im Vollschatten&lt;br /&gt;
    s = 0.0;&lt;br /&gt;
}&lt;br /&gt;
else if (rF_inner &amp;gt;= 0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
    // Wir befinden uns im Halbschatten, lineare Interpolation&lt;br /&gt;
    s = (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
    // Wir empfangen Licht von zwei Richtungen, also oben und unten am Occluder vorbei.&lt;br /&gt;
    // Die beiden rF heben sich gegenseitig auf.&lt;br /&gt;
    s = (-2*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24667</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24667"/>
				<updated>2010-01-31T12:33:00Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Funktionsweise */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Kommen wir nun zum eigentlichen Shader. Verwendet man eine Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines Kreiskegels. Da eine Punktlichtquelle aber unschöne harte Schatten wirft, wollen wir eine Kugellichtquelle verwenden. Eine Kugellichtquelle besteht aus unendlich vielen Punktlichtquellen die kugelförmig angeordnet sind. Da bei dieser Lichtquelle das Licht aus leicht unterschiedlichen Richtungen auf das Objekt fällt entstehen wie in der Realität weiche Schatten.&lt;br /&gt;
&lt;br /&gt;
Das Schatten-Volumen einer von einer Kugellichtquelle beleuchteten Kugel lässt sich durch zwei Kreiskegel beschreiben. Dies machen wir uns zu nutze. Wie auf dem folgenden Bild erkennbar gibt es einen inneren Kegel (''inner cone'') und einen äußeren Kegel (''outer cone''). Befinden wir uns außerhalb des äußeren Kegels erhalten wir die volle Lichtstärke, da der Occluder keine Auswirkungen hat. Innerhalb des inneren Kegels befinden wir uns dagegen vollständig im Schatten. Die dritte Möglichkeit ist eine Position innerhalb des äußeren aber außerhalb des inneren Kegels. Hier ist die Halbschattenzone die für den weichen Schatten sorgt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
Die Hauptachse beider Kegel liegt wie zu erkennen ist auf der Geraden durch Lichtquelle &amp;lt;tt&amp;gt;L&amp;lt;/tt&amp;gt; und Occluder &amp;lt;tt&amp;gt;O&amp;lt;/tt&amp;gt;. Im Shader beschreiben wir beide Kegel durch den Radius an zwei Punkten, nämlich den Radius auf Höhe der Lichtquelle und auf Höhe des Occluders. Für den äußeren Kegel bedienen wir uns eines Tricks, den wir nehmen für die Lichtquelle einfach einen negativen Radius an. Die Beschreibung beider Kegel ist also bis auf das Vorzeichen der Lichtquellenradius bei beiden Kegeln identisch. Wir haben damit genug Daten um den Radius an jeder Stelle beider Kegel zu berechnen.&lt;br /&gt;
&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; ist die Position des gerade gerenderten Fragments. Der erste Schritt besteht nun darin den Punkt &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; zu berechnen. Eine detaillierte Erklärung dazu findet sich im [[#Geometrie-Crashkurs|vorhergehenden Abschnitt]]. Den Abstand zwischen &amp;lt;tt&amp;gt;F&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;F'&amp;lt;/tt&amp;gt; bezeichnen wir als &amp;lt;tt&amp;gt;rF&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Nun geht es darum den Radius des inneren und äußeren Kegels (&amp;lt;tt&amp;gt;rF_inner&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;rF_outer&amp;lt;/tt&amp;gt;) auf Höhe von F' zu berechnen. Dies geht über den beliebten Dreisatz, die nötigen Formeln finden sich oben im Bild. Der einzige Unterschied bei beiden Formeln liegt nur im Vorzeichen für den Radius der Lichtquelle.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24666</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24666"/>
				<updated>2010-01-31T12:23:36Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Geometrie-Crashkurs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := A + n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Verwendet man eine gewöhnliche Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines einfachen Kreiskegels. Selbst wenn wir statt der Punktlichtquelle eine Kugellichtquelle verwenden lässt sich das Schattenvolumen noch recht leicht durch zwei Kegel beschreiben. Dies machen wir uns zu nutze. &lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:shader_ConeVolumeSoftShadow.png&amp;diff=24665</id>
		<title>Datei:shader ConeVolumeSoftShadow.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:shader_ConeVolumeSoftShadow.png&amp;diff=24665"/>
				<updated>2010-01-31T12:06:25Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: hat eine neue Version von „Datei:shader ConeVolumeSoftShadow.png“ hochgeladen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24664</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24664"/>
				<updated>2010-01-31T11:11:24Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Geometrie-Crashkurs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; normalisiert war lässt sich der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; nun leicht berechnen: &amp;lt;tt&amp;gt;B' := n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnet sich aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Verwendet man eine gewöhnliche Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines einfachen Kreiskegels. Selbst wenn wir statt der Punktlichtquelle eine Kugellichtquelle verwenden lässt sich das Schattenvolumen noch recht leicht durch zwei Kegel beschreiben. Dies machen wir uns zu nutze. &lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24663</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24663"/>
				<updated>2010-01-31T11:09:35Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Geometrie-Crashkurs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Wer meint das Skalarprodukt und seine Eigenschaften schon zu kennen, kann diesen Abschnitt einfach überspringen. Wenn etwas unklar ist kann man ja noch einmal hierher zurückkommen.&lt;br /&gt;
&lt;br /&gt;
Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; lässt sich nun leicht berechnen, da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; ja normalisiert war: &amp;lt;tt&amp;gt;B' := n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; folgt aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Verwendet man eine gewöhnliche Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines einfachen Kreiskegels. Selbst wenn wir statt der Punktlichtquelle eine Kugellichtquelle verwenden lässt sich das Schattenvolumen noch recht leicht durch zwei Kegel beschreiben. Dies machen wir uns zu nutze. &lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24662</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24662"/>
				<updated>2010-01-31T11:07:38Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Voraussetzungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann, wenn es sich bei sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welches Objekt auf welches andere Objekt einen Schatten wirft. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; lässt sich nun leicht berechnen, da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; ja normalisiert war: &amp;lt;tt&amp;gt;B' := n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; folgt aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Verwendet man eine gewöhnliche Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines einfachen Kreiskegels. Selbst wenn wir statt der Punktlichtquelle eine Kugellichtquelle verwenden lässt sich das Schattenvolumen noch recht leicht durch zwei Kegel beschreiben. Dies machen wir uns zu nutze. &lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24661</id>
		<title>shader ConeVolumeShadow</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=shader_ConeVolumeShadow&amp;diff=24661"/>
				<updated>2010-01-31T11:03:23Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: /* Geometrie-Crashkurs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Offline}}&lt;br /&gt;
&lt;br /&gt;
=Shadername=&lt;br /&gt;
Zurück zur [[Shadersammlung]]&lt;br /&gt;
{|{{Prettytable_B1}} width=100%&lt;br /&gt;
!width=60%|Beschreibung&lt;br /&gt;
!width=20%|Autor&lt;br /&gt;
!width=20%|Version&lt;br /&gt;
|-&lt;br /&gt;
|Kegelvolumen-Schatten&lt;br /&gt;
|Coolcat&lt;br /&gt;
|1.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Bilder==&lt;br /&gt;
{|&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow1.jpg|thumb|256px]]&lt;br /&gt;
| [[Bild:shader_ConeVolumeShadow2.jpg|thumb|256px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann wenn es sich sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welche Objekte auf welches Objekt einen Schatten werfen. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.&lt;br /&gt;
&lt;br /&gt;
== Geometrie-Crashkurs ==&lt;br /&gt;
Um den Shader zu verstehen ist ein wenig Geometrie-Wissen erforderlich, insbesondere bezüglich des Skalarproduktes. Das Skalarprodukt, häufig auch als Dot-Product bezeichnet, ist für zwei 3D-Vektoren &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; wie folgt definiert:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) := a.x*b.x + a.y*b.y + a.z*b.z&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Betrag &amp;lt;tt&amp;gt;|a|&amp;lt;/tt&amp;gt; eines Vektors &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; ist als dessen Länge bezüglich der euklidischen Norm definiert. Dies ist im wesentlichen der gute alte [http://de.wikipedia.org/wiki/Satz_des_Pythagoras Pythagoras] für drei Dimensionen:&lt;br /&gt;
:&amp;lt;tt&amp;gt;|a| := sqrt(a.x*a.x + a.y*a.y + a.z*a.z)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Eine wichtige Eigenschaft dieses Skalarproduktes ist, dass sich darüber der Winkel &amp;lt;tt&amp;gt;w&amp;lt;/tt&amp;gt; zwischen den beiden Vektoren bestimmen lässt. Es gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(a,b) = |a| * |b| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Dies wollen wir ausnutzen. In der folgenden Zeichnung sind und die Punkte &amp;lt;tt&amp;gt;A&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;C&amp;lt;/tt&amp;gt; bekannt. Wir wollen &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; und die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; berechnen, also &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; im rechten Winkel auf den Vektor &amp;lt;tt&amp;gt;AC&amp;lt;/tt&amp;gt; projizieren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeProjection.png]]&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Skalarproduktes ist dies recht einfach. Zunächst berechnen wir den Vektor &amp;lt;tt&amp;gt;AB := B - A&amp;lt;/tt&amp;gt;, sowie den Vektor &amp;lt;tt&amp;gt;AC := C - A&amp;lt;/tt&amp;gt;. Letzteren normalisieren wir, indem wir durch die Länge teilen: &amp;lt;tt&amp;gt;n := AC / |AC|&amp;lt;/tt&amp;gt;. Da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; die Länge 1 hat gilt:&lt;br /&gt;
:&amp;lt;tt&amp;gt;dot(AB, n) = |AB| * cos(w)&amp;lt;/tt&amp;gt;&lt;br /&gt;
Aus der Mittelstufe sollte bekannt sein, dass der [http://de.wikipedia.org/wiki/Kosinus#Geometrische_Definition Kosinus wie folgt definiert] ist:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
Die Hypotenuse ist hier die Strecke &amp;lt;tt&amp;gt;|AB|&amp;lt;/tt&amp;gt; und die Ankathete ist die gesuchte Strecke &amp;lt;tt&amp;gt;|AB'| := |B' - A|&amp;lt;/tt&amp;gt;. Es gilt also:&lt;br /&gt;
:&amp;lt;tt&amp;gt;cos(w) = Ankathete / Hypotenuse&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; cos(w) = |AB'| / |AB|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; |AB| * cos(w) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
:&amp;lt;tt&amp;gt;&amp;lt;=&amp;gt; dot(AB, n) = |AB'|&amp;lt;/tt&amp;gt;&lt;br /&gt;
Der Punkt &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt; lässt sich nun leicht berechnen, da &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; ja normalisiert war: &amp;lt;tt&amp;gt;B' := n * |AB'|&amp;lt;/tt&amp;gt;. Die Strecke &amp;lt;tt&amp;gt;h&amp;lt;/tt&amp;gt; folgt aus dem Abstand zwischen &amp;lt;tt&amp;gt;B&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;B'&amp;lt;/tt&amp;gt;, also &amp;lt;tt&amp;gt;h := |B - B'|&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Funktionsweise ==&lt;br /&gt;
Verwendet man eine gewöhnliche Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines einfachen Kreiskegels. Selbst wenn wir statt der Punktlichtquelle eine Kugellichtquelle verwenden lässt sich das Schattenvolumen noch recht leicht durch zwei Kegel beschreiben. Dies machen wir uns zu nutze. &lt;br /&gt;
&lt;br /&gt;
[[Bild:shader_ConeVolumeSoftShadow.png]]&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
====Vertexshader====&lt;br /&gt;
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform mat4 uModelViewProjection;&lt;br /&gt;
uniform mat4 uModelView;&lt;br /&gt;
uniform mat3 uNormalMatrix;&lt;br /&gt;
&lt;br /&gt;
attribute vec3 aPosition;&lt;br /&gt;
attribute vec3 aNormal;&lt;br /&gt;
attribute vec2 aTexCoord;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	gl_Position = uModelViewProjection * vec4(aPosition, 1.0);&lt;br /&gt;
	vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;&lt;br /&gt;
	vNormal = normalize(uNormalMatrix * aNormal);&lt;br /&gt;
	vTexCoord = aTexCoord;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Fragmentshader====&lt;br /&gt;
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion &amp;lt;tt&amp;gt;shadowFactor()&amp;lt;/tt&amp;gt; auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;uniform sampler2D uTexture;&lt;br /&gt;
&lt;br /&gt;
// the lightsource described by position and radius&lt;br /&gt;
uniform vec4 uLightsource;&lt;br /&gt;
vec3 lsPosition = uLightsource.xyz;&lt;br /&gt;
float lsRadius = uLightsource.w;&lt;br /&gt;
&lt;br /&gt;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4&lt;br /&gt;
const int shadowMaxCount = 5;&lt;br /&gt;
uniform vec4 uShadows[shadowMaxCount];&lt;br /&gt;
uniform int uShadowCount;&lt;br /&gt;
&lt;br /&gt;
varying vec3 vPosition;&lt;br /&gt;
varying vec3 vNormal;&lt;br /&gt;
varying vec2 vTexCoord;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
const vec3 cAmbient = vec3(0.025,0.025,0.025);&lt;br /&gt;
&lt;br /&gt;
// simple local diffuse per-pixel lighting&lt;br /&gt;
float diffuseFactor() {&lt;br /&gt;
	vec3 lightDir = normalize(lsPosition-vPosition);&lt;br /&gt;
	vec3 normal = normalize(vNormal);&lt;br /&gt;
	return max(dot(lightDir, normal), 0.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process all shadow volumes&lt;br /&gt;
float shadowFactor() {&lt;br /&gt;
	float s = 1.0;&lt;br /&gt;
	// iterate all shadow volumes&lt;br /&gt;
	for (int i=0; i&amp;lt;uShadowCount; ++i) {&lt;br /&gt;
		// extract data&lt;br /&gt;
		vec4 occluder = uShadows[i];&lt;br /&gt;
		vec3 ocPosition = occluder.xyz;&lt;br /&gt;
		float ocRadius = occluder.w;&lt;br /&gt;
&lt;br /&gt;
		// project fragment (vPosition) on the cone axis =&amp;gt; F_&lt;br /&gt;
		vec3 nvLO = ocPosition - lsPosition;&lt;br /&gt;
		float dLO = length(nvLO);&lt;br /&gt;
		nvLO /= dLO;&lt;br /&gt;
		vec3 vLF = vPosition - lsPosition;&lt;br /&gt;
		float dLF_ = dot(vLF, nvLO);&lt;br /&gt;
		if (dLF_ &amp;lt; dLO) {&lt;br /&gt;
			// fragment before occluder =&amp;gt; no shadow&lt;br /&gt;
			continue; &lt;br /&gt;
		}&lt;br /&gt;
		vec3 F_ = lsPosition + dLF_ * nvLO;&lt;br /&gt;
		float rF = distance(F_, vPosition);&lt;br /&gt;
&lt;br /&gt;
		// compute outer and inner radius at F_&lt;br /&gt;
		float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;&lt;br /&gt;
		if (rF &amp;gt;= rF_outer) {&lt;br /&gt;
			// outside the outer cone =&amp;gt; no shadow&lt;br /&gt;
			continue;&lt;br /&gt;
		} &lt;br /&gt;
		float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;&lt;br /&gt;
		if (rF_inner &amp;gt;= rF) {&lt;br /&gt;
			// inside the inner cone =&amp;gt; full shadow&lt;br /&gt;
			return 0.0; &lt;br /&gt;
		}&lt;br /&gt;
		else if (rF_inner &amp;gt;= 0.0 || rF &amp;gt;= -rF_inner) {&lt;br /&gt;
			// soft shadow, linear interpolation&lt;br /&gt;
			s *= (rF - rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
		else {&lt;br /&gt;
			// light from both sides of the occluder&lt;br /&gt;
			s *= (-2.0*rF_inner) / (rF_outer - rF_inner);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main() {&lt;br /&gt;
	vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;&lt;br /&gt;
	gl_FragColor.xyz = cAmbient * texcolor;&lt;br /&gt;
	float lightFactor = diffuseFactor();&lt;br /&gt;
	if (lightFactor &amp;gt; 0.004) {&lt;br /&gt;
		// don't compute shadows for fragments that are already dark from local lighting&lt;br /&gt;
		lightFactor *= shadowFactor();&lt;br /&gt;
	}&lt;br /&gt;
	gl_FragColor.xyz += texcolor * lightFactor;&lt;br /&gt;
	gl_FragColor.w = 1.0;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Datei:shader_ConeVolumeProjection.png&amp;diff=24660</id>
		<title>Datei:shader ConeVolumeProjection.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Datei:shader_ConeVolumeProjection.png&amp;diff=24660"/>
				<updated>2010-01-31T10:59:19Z</updated>
		
		<summary type="html">&lt;p&gt;Coolcat: hat eine neue Version von „Datei:shader ConeVolumeProjection.png“ hochgeladen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Coolcat</name></author>	</entry>

	</feed>