GLS.ShaderCel.pas 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLS.ShaderCel;
  5. (*
  6. A shader that applies cel shading through a vertex program
  7. and shade definition texture.
  8. *)
  9. interface
  10. {$I GLScene.inc}
  11. uses
  12. Winapi.OpenGL,
  13. Winapi.OpenGLext,
  14. System.Classes,
  15. System.SysUtils,
  16. OpenGLTokens,
  17. GLTexture,
  18. GLContext,
  19. GLGraphics,
  20. GLS.Utils,
  21. GLVectorGeometry,
  22. GLColor,
  23. GLRenderContextInfo,
  24. GLMaterial,
  25. GLState,
  26. GLTextureFormat;
  27. type
  28. (*Cel shading options.
  29. csoOutlines: Render a second outline pass.
  30. csoTextured: Allows for a primary texture that the cel shading
  31. is modulated with and forces the shade definition
  32. to render as a second texture. *)
  33. TGLCelShaderOption = (csoOutlines, csoTextured, csoNoBuildShadeTexture);
  34. TGLCelShaderOptions = set of TGLCelShaderOption;
  35. // An event for user defined cel intensity.
  36. TGLCelShaderGetIntensity = procedure(Sender: TObject; var intensity: Byte) of
  37. object;
  38. // A generic cel shader.
  39. TGLCelShader = class(TGLShader)
  40. private
  41. FOutlineWidth: Single;
  42. FCelShaderOptions: TGLCelShaderOptions;
  43. FVPHandle: TGLARBVertexProgramHandle;
  44. FShadeTexture: TGLTexture;
  45. FOnGetIntensity: TGLCelShaderGetIntensity;
  46. FOutlinePass,
  47. FUnApplyShadeTexture: Boolean;
  48. FOutlineColor: TGLColor;
  49. protected
  50. procedure SetCelShaderOptions(const val: TGLCelShaderOptions);
  51. procedure SetOutlineWidth(const val: Single);
  52. procedure SetOutlineColor(const val: TGLColor);
  53. procedure BuildShadeTexture;
  54. procedure Loaded; override;
  55. function GenerateVertexProgram: string;
  56. public
  57. constructor Create(AOwner: TComponent); override;
  58. destructor Destroy; override;
  59. procedure DoApply(var rci: TGLRenderContextInfo; Sender: TObject); override;
  60. function DoUnApply(var rci: TGLRenderContextInfo): Boolean; override;
  61. property ShadeTexture: TGLTexture read FShadeTexture;
  62. published
  63. property CelShaderOptions: TGLCelShaderOptions read FCelShaderOptions write
  64. SetCelShaderOptions;
  65. property OutlineColor: TGLColor read FOutlineColor write SetOutlineColor;
  66. property OutlineWidth: Single read FOutlineWidth write SetOutlineWidth;
  67. property OnGetIntensity: TGLCelShaderGetIntensity read FOnGetIntensity write
  68. FOnGetIntensity;
  69. end;
  70. // ------------------------------------------------------------------
  71. implementation
  72. // ------------------
  73. // ------------------ TGLCelShader ------------------
  74. // ------------------
  75. constructor TGLCelShader.Create(AOwner: TComponent);
  76. begin
  77. inherited;
  78. FOutlineWidth := 3;
  79. FCelShaderOptions := [csoOutlines];
  80. FShadeTexture := TGLTexture.Create(Self);
  81. with FShadeTexture do
  82. begin
  83. Enabled := True;
  84. MinFilter := miNearest;
  85. MagFilter := maNearest;
  86. TextureWrap := twNone;
  87. TextureMode := tmModulate;
  88. end;
  89. FOutlineColor := TGLColor.Create(Self);
  90. FOutlineColor.OnNotifyChange := NotifyChange;
  91. FOutlineColor.Initialize(clrBlack);
  92. ShaderStyle := ssLowLevel;
  93. FVPHandle := TGLARBVertexProgramHandle.Create;
  94. end;
  95. destructor TGLCelShader.Destroy;
  96. begin
  97. FVPHandle.Free;
  98. FShadeTexture.Free;
  99. FOutlineColor.Free;
  100. inherited;
  101. end;
  102. procedure TGLCelShader.Loaded;
  103. begin
  104. inherited;
  105. BuildShadeTexture;
  106. end;
  107. procedure TGLCelShader.BuildShadeTexture;
  108. var
  109. bmp32: TGLBitmap32;
  110. i: Integer;
  111. intensity: Byte;
  112. begin
  113. if csoNoBuildShadeTexture in FCelShaderOptions then
  114. exit;
  115. with FShadeTexture do
  116. begin
  117. ImageClassName := 'TGLBlankImage';
  118. TGLBlankImage(Image).Width := 128;
  119. TGLBlankImage(Image).Height := 2;
  120. end;
  121. bmp32 := FShadeTexture.Image.GetBitmap32;
  122. bmp32.Blank := false;
  123. for i := 0 to bmp32.Width - 1 do
  124. begin
  125. intensity := i * (256 div bmp32.Width);
  126. if Assigned(FOnGetIntensity) then
  127. FOnGetIntensity(Self, intensity)
  128. else
  129. begin
  130. if intensity > 230 then
  131. intensity := 255
  132. else if intensity > 150 then
  133. intensity := 230
  134. else if intensity > 100 then
  135. intensity := intensity + 50
  136. else
  137. intensity := 150;
  138. end;
  139. bmp32.Data^[i].r := intensity;
  140. bmp32.Data^[i].g := intensity;
  141. bmp32.Data^[i].b := intensity;
  142. bmp32.Data^[i].a := 1;
  143. bmp32.Data^[i + bmp32.Width] := bmp32.Data^[i];
  144. end;
  145. end;
  146. function TGLCelShader.GenerateVertexProgram: string;
  147. var
  148. VP: TStringList;
  149. begin
  150. VP := TStringList.Create;
  151. VP.Add('!!ARBvp1.0');
  152. VP.Add('OPTION ARB_position_invariant;');
  153. VP.Add('PARAM mvinv[4] = { state.matrix.modelview.inverse };');
  154. VP.Add('PARAM lightPos = program.local[0];');
  155. VP.Add('TEMP temp, light, normal;');
  156. VP.Add(' DP4 light.x, mvinv[0], lightPos;');
  157. VP.Add(' DP4 light.y, mvinv[1], lightPos;');
  158. VP.Add(' DP4 light.z, mvinv[2], lightPos;');
  159. VP.Add(' ADD light, light, -vertex.position;');
  160. VP.Add(' DP3 temp.x, light, light;');
  161. VP.Add(' RSQ temp.x, temp.x;');
  162. VP.Add(' MUL light, temp.x, light;');
  163. VP.Add(' DP3 temp, vertex.normal, vertex.normal;');
  164. VP.Add(' RSQ temp.x, temp.x;');
  165. VP.Add(' MUL normal, temp.x, vertex.normal;');
  166. VP.Add(' MOV result.color, state.material.diffuse;');
  167. if csoTextured in FCelShaderOptions then
  168. begin
  169. VP.Add(' MOV result.texcoord[0], vertex.texcoord[0];');
  170. VP.Add(' DP3 result.texcoord[1].x, normal, light;');
  171. end
  172. else
  173. begin
  174. VP.Add(' DP3 result.texcoord[0].x, normal, light;');
  175. end;
  176. VP.Add('END');
  177. Result := VP.Text;
  178. VP.Free;
  179. end;
  180. procedure TGLCelShader.DoApply(var rci: TGLRenderContextInfo; Sender: TObject);
  181. var
  182. light: TVector;
  183. begin
  184. if (csDesigning in ComponentState) then
  185. exit;
  186. FVPHandle.AllocateHandle;
  187. if FVPHandle.IsDataNeedUpdate then
  188. begin
  189. FVPHandle.LoadARBProgram(GenerateVertexProgram);
  190. Enabled := FVPHandle.Ready;
  191. FVPHandle.NotifyDataUpdated;
  192. if not Enabled then
  193. Abort;
  194. end;
  195. rci.GLStates.Disable(stLighting);
  196. gl.GetLightfv(GL_LIGHT0, GL_POSITION, @light.X);
  197. FVPHandle.Enable;
  198. FVPHandle.Bind;
  199. gl.ProgramLocalParameter4fv(GL_VERTEX_PROGRAM_NV, 0, @light.X);
  200. if (csoTextured in FCelShaderOptions) then
  201. FShadeTexture.ApplyAsTexture2(rci, nil)
  202. else
  203. FShadeTexture.Apply(rci);
  204. FOutlinePass := csoOutlines in FCelShaderOptions;
  205. FUnApplyShadeTexture := True;
  206. end;
  207. function TGLCelShader.DoUnApply(var rci: TGLRenderContextInfo): Boolean;
  208. begin
  209. Result := False;
  210. if (csDesigning in ComponentState) then
  211. exit;
  212. FVPHandle.Disable;
  213. if FUnApplyShadeTexture then
  214. begin
  215. if (csoTextured in FCelShaderOptions) then
  216. FShadeTexture.UnApplyAsTexture2(rci, false)
  217. else
  218. FShadeTexture.UnApply(rci);
  219. FUnApplyShadeTexture := False;
  220. end;
  221. if FOutlinePass then
  222. with rci.GLStates do
  223. begin
  224. ActiveTexture := 0;
  225. ActiveTextureEnabled[ttTexture2D] := False;
  226. Enable(stBlend);
  227. Enable(stLineSmooth);
  228. Disable(stLineStipple);
  229. Enable(stCullFace);
  230. PolygonMode := pmLines;
  231. LineWidth := FOutlineWidth;
  232. CullFaceMode := cmFront;
  233. LineSmoothHint := hintNicest;
  234. SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  235. DepthFunc := cfLEqual;
  236. gl.Color4fv(FOutlineColor.AsAddress);
  237. Result := True;
  238. FOutlinePass := False;
  239. Exit;
  240. end
  241. else
  242. with rci.GLStates do
  243. begin
  244. rci.GLStates.PolygonMode := pmFill;
  245. rci.GLStates.CullFaceMode := cmBack;
  246. rci.GLStates.DepthFunc := cfLEqual;
  247. end;
  248. end;
  249. procedure TGLCelShader.SetCelShaderOptions(const val: TGLCelShaderOptions);
  250. begin
  251. if val <> FCelShaderOptions then
  252. begin
  253. FCelShaderOptions := val;
  254. BuildShadeTexture;
  255. FVPHandle.NotifyChangesOfData;
  256. NotifyChange(Self);
  257. end;
  258. end;
  259. procedure TGLCelShader.SetOutlineWidth(const val: Single);
  260. begin
  261. if val <> FOutlineWidth then
  262. begin
  263. FOutlineWidth := val;
  264. NotifyChange(Self);
  265. end;
  266. end;
  267. procedure TGLCelShader.SetOutlineColor(const val: TGLColor);
  268. begin
  269. if val <> FOutlineColor then
  270. begin
  271. FOutlineColor.Assign(val);
  272. NotifyChange(Self);
  273. end;
  274. end;
  275. end.