Sun.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. SunClass Sun;
  6. Bool AstrosDraw=true;
  7. Memc<Astro> Astros;
  8. /******************************************************************************/
  9. Astro::Astro()
  10. {
  11. draw=true;
  12. blend=true;
  13. glow=0;
  14. size=0.15f;
  15. pos.set(-SQRT3_3, SQRT3_3, -SQRT3_3);
  16. image_color=WHITE;
  17. light_color =0;
  18. light_vol =0; // 0.2f
  19. light_vol_exp =1.0f;
  20. light_vol_steam=0.5f;
  21. }
  22. /******************************************************************************/
  23. void Astro::light()
  24. {
  25. if(is())
  26. {
  27. LightDir(-pos, light_color, light_vol, light_vol_exp, light_vol_steam).add(true, this);
  28. }
  29. }
  30. void Astro::Draw()
  31. {
  32. if(is() && Frustum(BallM(D.viewRange()*size*SQRT2, CamMatrix.pos+pos*D.viewRange())))
  33. {
  34. // TODO: apply per-pixel softing based on depth buffer, exactly like particle softing (draw closer to camera, but scale XY size, along CamMatrix.xy) and modify pixel shader
  35. Renderer._has_glow|=(glow!=0);
  36. D .alphaFactor(VecB4(0, 0, 0, glow));
  37. D .alpha (blend ? ALPHA_BLEND_FACTOR : ALPHA_ADD_FACTOR);
  38. VI.image (image());
  39. VI.setType (VI_3D_TEX_COL, VI_STRIP);
  40. if(Vtx3DTexCol *v=(Vtx3DTexCol*)VI.addVtx(4))
  41. {
  42. Matrix3 m; m.setRotation(Vec(0, 1, 0), pos); // use top as original position, because this makes the most realistic UV rotation when the object rotates around the sky
  43. C Vec &right=(m.x*=size),
  44. &up =(m.z*=size);
  45. v[0].pos=pos-right+up;
  46. v[1].pos=pos+right+up;
  47. v[2].pos=pos-right-up;
  48. v[3].pos=pos+right-up;
  49. Flt depth=0; REP(4)MAX(depth, Dot(v[i].pos, CamMatrix.z)); // project onto viewport far plane, so it's always at the back, need to find max depth of all vertexes, because if vertexes would be projected individually, then UV would be stretched unrealistically
  50. Flt range=D.viewRange()-0.01f, // make it smaller to avoid being clipped by viewport far plane
  51. scale=range/depth;
  52. REP(4)v[i].pos*=scale;
  53. v[0].color=v[1].color=v[2].color=v[3].color=image_color;
  54. v[0].tex.set( 0, 0);
  55. v[1].tex.set(image->_part.x, 0);
  56. v[2].tex.set( 0, image->_part.y);
  57. v[3].tex.set(image->_part.x, image->_part.y);
  58. }
  59. VI.end();
  60. MaterialClear(); // because alpha factor
  61. }
  62. }
  63. /******************************************************************************/
  64. SunClass::SunClass()
  65. {
  66. glow=128;
  67. light_color=0.7f;
  68. highlight_front=0.10f;
  69. highlight_back =0.07f;
  70. rays_color =0.05f; // don't set more than 0.1f because it will require jittering
  71. rays_jitter =-1 ; // auto
  72. rays_mode =SUN_RAYS_HIGH;
  73. _rays_res =FltToByteScale(0.25f);
  74. _rays_mask_res=FltToByteScale(1.0f ); // we can use full res because it makes a small performance difference
  75. }
  76. Bool SunClass::wantRays()C {return is() && rays_mode && rays_color.max()>EPS_COL;}
  77. Flt SunClass::raysRes( )C {return ByteScaleToFlt(_rays_res);}
  78. SunClass& SunClass::raysRes(Flt scale) {Byte res=FltToByteScale(scale); if(res!=_rays_res){_rays_res=res; Renderer.rtClean();} return T;}
  79. Flt SunClass::raysMaskRes( )C {return ByteScaleToFlt(_rays_mask_res);}
  80. SunClass& SunClass::raysMaskRes(Flt scale) {Byte res=FltToByteScale(scale); if(res!=_rays_mask_res){_rays_mask_res=res; Renderer.rtClean();} return T;}
  81. #pragma pack(push, 4)
  82. struct GpuSun
  83. {
  84. Vec2 pos2;
  85. Vec pos, color;
  86. };
  87. #pragma pack(pop)
  88. INLINE Shader* GetSunRaysMask(Bool mask ) {Shader* &s=Sh.h_SunRaysMask[mask] ; if(SLOW_SHADER_LOAD && !s)s=Sh.getSunRaysMask(mask ); return s;}
  89. INLINE Shader* GetSunRays (Bool high, Bool jitter) {Shader* &s=Sh.h_SunRays [high][jitter]; if(SLOW_SHADER_LOAD && !s)s=Sh.getSunRays (high, jitter); return s;}
  90. void SunClass::drawRays(Image *coverage, Vec &color)
  91. {
  92. GpuSun sun;
  93. sun.pos =pos*CamMatrixInv.orn();
  94. sun.pos2=D.screenToUV(_pos2);
  95. Flt alpha=Sat(sun.pos.z/0.18f); // if sun is close to camera (or behind it) then decrease opacity
  96. if(Fog.draw && Fog.affect_sky)alpha*=VisibleOpacity(Fog.density, D.viewRange()); // rays are in ALPHA_ADD mode, so we need to downscale the color
  97. color=rays_color*alpha;
  98. if(Renderer._cur[0]!=Renderer._col()) // if rendering to a separate RT, then render with full color, so later when upscaling, we can use dithering with higher precision, during dithering the color will be set to the right value
  99. {
  100. sun.color=1;
  101. }else // if rendering to the main target, then use final color
  102. {
  103. sun.color=color;
  104. }
  105. REPS(Renderer._eye, Renderer._eye_num)
  106. {
  107. Rect *rect=Renderer.setEyeParams(), ext_rect; if(!D._view_main.full){ext_rect=*rect; rect=&ext_rect.extend(Renderer.pixelToScreenSize(1/*+(rays_soft?SHADER_BLUR_RANGE:0)*/));} // always extend by 1 because we may apply this RT to the final RT usin linear filtering
  108. if(Renderer._stereo)
  109. {
  110. sun.pos =pos; sun.pos.divNormalized(ActiveCam.matrix.orn());
  111. sun.pos2 =D.screenToUV(_pos2);
  112. sun.pos2.x+=ProjMatrixEyeOffset[Renderer._eye]*0.5f;
  113. sun.pos2.x*=0.5f;
  114. if(Renderer._eye)sun.pos2.x+=0.5f;
  115. }
  116. Sh.h_Sun->set(sun);
  117. Bool jitter=((rays_jitter<0) ? rays_color.max()>0.1f+EPS_COL : rays_jitter!=0); // for auto, enable jittering only if rays have a high brightness
  118. switch(_actual_rays_mode)
  119. {
  120. case SUN_RAYS_HIGH: GetSunRays(true , jitter)->draw(coverage , rect); break;
  121. default : GetSunRays(false, jitter)->draw(Renderer._ds_1s, rect); break;
  122. }
  123. }
  124. }
  125. /******************************************************************************/
  126. void AstroPrepare()
  127. {
  128. Sun._actual_rays_mode=SUN_RAYS_OFF;
  129. if(AstrosDraw)
  130. {
  131. if(Sun.wantRays()
  132. && Renderer.canReadDepth() && !Renderer.mirror()
  133. && PosToFullScreen(CamMatrix.pos+Sun.pos*D.viewRange(), Sun._pos2) && FovPerspective(D.viewFovMode())
  134. && !(Fog.draw && Fog.affect_sky && VisibleOpacity(Fog.density, D.viewRange())<=EPS_COL) // if fog is too dense then don't draw sun rays
  135. )
  136. {
  137. if(Sun.rays_mode==SUN_RAYS_HIGH && D._mrt_post_process && D._max_rt>=2)
  138. {
  139. Renderer._sky_coverage.get(ImageRTDesc(Renderer._col->w(), Renderer._col->h(), IMAGERT_ONE, Renderer._col->samples()));
  140. Renderer._sky_coverage->clearViewport(1); // set full initially, which we will decrease with clouds and finally depth tests
  141. Sun._actual_rays_mode=SUN_RAYS_HIGH;
  142. }else Sun._actual_rays_mode=SUN_RAYS_LOW;
  143. }
  144. Sun.light(); FREPAO(Astros).light();
  145. }
  146. }
  147. void AstroDraw()
  148. {
  149. if(AstrosDraw)
  150. {
  151. Renderer.set(Renderer._col(), Renderer._ds(), true); // use DS for depth tests
  152. SetOneMatrix(MatrixM(CamMatrix.pos)); // normally we have to set matrixes after 'setEyeViewport', however since matrixes are always relative to the camera, and here we set exactly at the camera position, so the matrix will be the same for both eyes
  153. Sh.h_SkyFracMulAdd->set(Vec2(0, 1)); // astronomical objects are drawn as billboards which make use of sky fraction, so be sure to disable it before drawing
  154. D.depthWrite(false); D.depthFunc(FUNC_LESS_EQUAL); REPS(Renderer._eye, Renderer._eye_num){Renderer.setEyeViewport(); Sun.Draw(); FREPAO(Astros).Draw();}
  155. D.depthWrite(true ); D.depthFunc(FUNC_LESS );
  156. }
  157. }
  158. Bool AstroDrawRays()
  159. {
  160. if(Sun._actual_rays_mode)
  161. {
  162. Renderer.downSample(); // we're modifying existing RT, so downSample if needed
  163. if(Renderer._sky_coverage) // apply mask based on depth tests, have to do this at the end, once we have depth buffer set (including from blend phase), no need to check for 'canReadDepth' because that was already checked when setting '_sky_coverage'
  164. {
  165. const VecI2 res=ByteScaleRes(Renderer.fx(), Sun._rays_mask_res);
  166. if(res!=Renderer._sky_coverage()->size()) // draw to smaller RT, and combine sky coverage with depth tests
  167. {
  168. D.alpha(ALPHA_NONE);
  169. ImageRTPtr temp; temp.get(ImageRTDesc(res.x, res.y, IMAGERT_ONE));
  170. Renderer.set(temp(), null, true);
  171. GetSunRaysMask(true)->draw(Renderer._sky_coverage);
  172. Swap(temp, Renderer._sky_coverage);
  173. }else // apply depth tests to existing sky coverage
  174. {
  175. Renderer.set(Renderer._sky_coverage(), Renderer._ds(), true, NEED_DEPTH_READ); // '_ds' needed for 'depth2D' optimization
  176. D.alpha (ALPHA_MUL);
  177. D.depth2DOn (); GetSunRaysMask(false)->draw();
  178. D.depth2DOff();
  179. }
  180. if(Renderer.stage==RS_SKY_COVERAGE && Renderer.set(Renderer._sky_coverage))return true;
  181. }
  182. const VecI2 res=ByteScaleRes(Renderer.fx(), Sun._rays_res);
  183. ImageRTPtr rt0; if(res!=Renderer._col->size())rt0.get(ImageRTDesc(res.x, res.y, IMAGERT_ONE));
  184. Renderer.set(rt0 ? rt0() : Renderer._col(), null, rt0==null); // set viewport only when drawing to 'Renderer._col'
  185. SetOneMatrix();
  186. D.alpha(rt0 ? ALPHA_NONE : ALPHA_ADD); // if drawing to rt0 then set, if drawing to Renderer._col then add
  187. Vec color; Sun.drawRays(Renderer._sky_coverage(), color);
  188. if(rt0)
  189. {
  190. /*if(Sun.rays_soft && shift>=2)
  191. {
  192. if(!Sh.h_BlurX_X)
  193. {
  194. Sh.h_BlurX_X=Sh.get("BlurX_X");
  195. Sh.h_BlurX_W=Sh.get("BlurX_W");
  196. Sh.h_BlurY_X=Sh.get("BlurY_X");
  197. Sh.h_BlurY_W=Sh.get("BlurY_W");
  198. }
  199. D.alpha(ALPHA_NONE);
  200. rt1.get(ImageRTDesc(Renderer.fxW()>>shift, Renderer.fxH()>>shift, IMAGERT_ONE));
  201. Renderer.set(rt1(), null, false); Sh.GPU_API(h_BlurX_W, h_BlurX_X, h_BlurX_X)->draw(rt0(), &D.viewRect()); // DX9 uses A8 while others use R8 RT
  202. Renderer.set(rt0(), null, false); Sh.GPU_API(h_BlurY_W, h_BlurY_X, h_BlurY_X)->draw(rt1(), &D.viewRect()); // DX9 uses A8 while others use R8 RT
  203. }*/
  204. Renderer.set(Renderer._col(), null, true);
  205. D.alpha(ALPHA_ADD);
  206. Sh.h_Color[0]->set(Vec4(color, 0)); // we've rendered the sun with full intensity, so now we need to make it darker
  207. Sh.h_Color[1]->set(Vec4Zero );
  208. Bool dither=(D.dither() && !Renderer._col->highPrecision()); // don't do dithering for high precision RT
  209. Shader *shader;
  210. //if(Sun.rays_soft && shift==1)shader=Sh.h_SunRaysSoft;else
  211. if(dither )shader=Sh.GPU_API(h_DrawTexWCD, h_DrawTexXCD, h_DrawTexXCD);else // DX9 uses A8 while others use R8 RT
  212. shader=Sh.GPU_API(h_DrawTexWC , h_DrawTexXC , h_DrawTexXC ); // DX9 uses A8 while others use R8 RT
  213. REPS(Renderer._eye, Renderer._eye_num)shader->draw(rt0, Renderer._stereo ? &D._view_eye_rect[Renderer._eye] : &D.viewRect());
  214. }
  215. Renderer._sky_coverage.clear();
  216. }
  217. return false;
  218. }
  219. /******************************************************************************/
  220. }
  221. /******************************************************************************/