Skip to content

Commit ad5200b

Browse files
committed
Improved perspective camera implementation.
Added down sampling option.
1 parent 4e5749b commit ad5200b

File tree

1 file changed

+130
-53
lines changed

1 file changed

+130
-53
lines changed

PixelCamera.cs

Lines changed: 130 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ public class PixelCamera : MonoBehaviour
1010
[Serializable]
1111
protected class AdvancedSettings
1212
{
13+
[Tooltip("Material to draw output render with")]
1314
public Material cameraMaterial;
15+
[Tooltip("Stretches output display, for non square pixels")]
1416
public Vector2 aspectStretch = Vector2.one;
17+
[Tooltip("Scales down camera render size")]
18+
public float downSample = 1;
19+
[Tooltip("Z distance to draw as pixel perfect for perspective camera.")]
1520
public float perspectiveZ = 10;
1621
}
1722

@@ -20,34 +25,46 @@ protected struct CamSettings
2025
public int[] screenSize;
2126
public Vector2 aspect;
2227
public float zoomLevel;
28+
public float pixelsPerUnit;
29+
public float zDistance;
30+
public float downsample;
2331
public float fieldOfView;
32+
public float farPlane;
2433
public bool isOrtho;
2534

26-
public CamSettings(Vector2 aspect, float zoomLevel, float fieldOfView, bool isOrtho)
35+
public CamSettings(PixelCamera pixelCam, Camera cam)
2736
{
2837
screenSize = new[] {Screen.width, Screen.height};
29-
this.aspect = aspect;
30-
this.zoomLevel = zoomLevel;
31-
this.fieldOfView = fieldOfView;
32-
this.isOrtho = isOrtho;
38+
this.aspect = pixelCam.AspectStretch;
39+
this.zoomLevel = pixelCam.ZoomLevel;
40+
this.pixelsPerUnit = pixelCam.pixelsPerUnit;
41+
this.zDistance = pixelCam.PerspectiveZ;
42+
this.downsample = pixelCam.DownSample;
43+
this.fieldOfView = cam.fieldOfView;
44+
this.isOrtho = cam.orthographic;
45+
this.farPlane = cam.farClipPlane;
3346
}
3447

3548
public bool Equals(CamSettings other)
3649
{
3750
bool equalScreen = other.screenSize[0] == screenSize[0] &&
3851
other.screenSize[1] == screenSize[1];
3952
bool equalAspect = other.aspect == aspect;
40-
bool equalFoV = Math.Abs(other.fieldOfView - fieldOfView) <= float.Epsilon;
41-
bool equalZoom = Math.Abs(other.zoomLevel - zoomLevel) <= float.Epsilon;
42-
bool equalOrtho = other.isOrtho == isOrtho;
43-
bool isEqual = equalScreen && equalAspect &&
44-
equalFoV && equalZoom &&
45-
equalOrtho;
46-
//if (!isEqual)
47-
//{
48-
// Debug.LogFormat("scr {0}, asp {1}, fov {2}, zoom {3}", equalScreen, equalAspect, equalFoV, equalZoom);
49-
// Debug.LogFormat("Aspect: {0}, Other: {1}", aspect, other.aspect);
50-
//}
53+
54+
bool isEqual = other.isOrtho == isOrtho &&
55+
equalScreen &&
56+
equalAspect &&
57+
Mathf.Approximately(other.zoomLevel, zoomLevel) &&
58+
Mathf.Approximately(other.pixelsPerUnit, pixelsPerUnit) &&
59+
Mathf.Approximately(other.downsample, downsample);
60+
61+
if (isEqual && isOrtho == false)
62+
{
63+
isEqual &= Mathf.Approximately(other.zDistance, zDistance) &&
64+
Mathf.Approximately(other.fieldOfView, fieldOfView) &&
65+
Mathf.Approximately(other.farPlane, farPlane);
66+
}
67+
5168
return isEqual;
5269
}
5370
}
@@ -73,6 +90,9 @@ public bool Equals(CamSettings other)
7390
public Vector2 QuadMin { get; protected set; }
7491
public Vector2 QuadMax { get; protected set; }
7592

93+
/// <summary>
94+
/// Material to draw output render with
95+
/// </summary>
7696
public Material CameraMaterial
7797
{
7898
get
@@ -107,22 +127,29 @@ public float PixelsPerUnit
107127
set { pixelsPerUnit = value; }
108128
}
109129

130+
/// <summary>
131+
/// For perspective cameras. Z distance between near and far plane to scale as pixel perfect.
132+
/// </summary>
110133
public float PerspectiveZ
111134
{
112135
get
113136
{
114137
if (advancedSettings == null)
115138
return cam.farClipPlane*0.5f;
116-
return advancedSettings.perspectiveZ;
139+
advancedSettings.perspectiveZ = Mathf.Clamp(advancedSettings.perspectiveZ, cam.nearClipPlane, cam.farClipPlane);
140+
return advancedSettings.perspectiveZ;
117141
}
118142
set
119143
{
120144
if (advancedSettings == null)
121145
return;
122-
advancedSettings.perspectiveZ = value;
146+
advancedSettings.perspectiveZ = Mathf.Clamp(value, cam.nearClipPlane, cam.farClipPlane); ;
123147
}
124148
}
125149

150+
/// <summary>
151+
/// Stretches output display, for non square pixels
152+
/// </summary>
126153
public Vector2 AspectStretch
127154
{
128155
get
@@ -139,8 +166,44 @@ public Vector2 AspectStretch
139166
}
140167
}
141168

169+
/// <summary>
170+
/// Scales down camera render size. Clamped at minimum value of 1.
171+
/// </summary>
172+
public float DownSample
173+
{
174+
get
175+
{
176+
if (advancedSettings == null)
177+
return 1f;
178+
advancedSettings.downSample = Mathf.Max(1f, advancedSettings.downSample);
179+
return advancedSettings.downSample;
180+
}
181+
set
182+
{
183+
if (advancedSettings == null)
184+
return;
185+
advancedSettings.downSample = Mathf.Max(1f, value);
186+
}
187+
}
188+
189+
/// <summary>
190+
/// The render texture camera is being drawn into
191+
/// </summary>
142192
public RenderTexture RenderTexture { get { return renderTexture; } }
143-
public int[] CameraSize { get { return new int[] {renderTexture.width, renderTexture.height}; } }
193+
194+
/// <summary>
195+
/// Pixel size of the camera
196+
/// </summary>
197+
public int[] CameraSize
198+
{
199+
get
200+
{
201+
if (renderTexture == null)
202+
return new[] {0, 0};
203+
204+
return new[] {renderTexture.width, renderTexture.height};
205+
}
206+
}
144207

145208
private void Reset()
146209
{
@@ -167,7 +230,11 @@ private void Start()
167230

168231
protected virtual void OnEnable()
169232
{
170-
lastSettings = new CamSettings(AspectStretch, 0, cam.fieldOfView, cam.orthographic);
233+
lastSettings = new CamSettings(this, cam)
234+
{
235+
screenSize = new []{0, 0}
236+
};
237+
ForceRefresh();
171238

172239
falseCamGO = new GameObject("False Camera") {hideFlags = HideFlags.HideAndDontSave};
173240
falseCam = falseCamGO.AddComponent<Camera>();
@@ -205,40 +272,15 @@ protected virtual void OnDisable()
205272
falseCam = null;
206273
}
207274

208-
protected Vector2 GetScreenRenderSize()
275+
private void SetupCamera(CamSettings settings)
209276
{
210-
// For orthographic camera, physical render size is based on screen pixels
211-
Vector2 screenRenderSize = new Vector2(Screen.width, Screen.height);
212-
screenRenderSize /= zoomLevel;
213-
214-
// For perspective camera, physical render is based on world unit height
215-
// in terms of fustrum distance, converted to pixels
216-
if (cam.orthographic == false)
217-
{
218-
cam.aspect = (float) Screen.width / Screen.height;
219-
220-
float zDistance = PerspectiveZ;
221-
222-
var frustumHeight = 2.0f * zDistance * Mathf.Tan(cam.fieldOfView * 0.5f * Mathf.Deg2Rad);
223-
var frustumWidth = frustumHeight* cam.aspect;
224-
225-
screenRenderSize.x = frustumWidth;
226-
screenRenderSize.y = frustumHeight;
227-
screenRenderSize *= pixelsPerUnit;
228-
}
229-
230-
return screenRenderSize;
231-
}
232-
233-
private void SetupCamera()
234-
{
235-
var aspect = AspectStretch;
277+
var aspect = settings.aspect;
236278

237279
zoomLevel = Mathf.Max(0.05f, Mathf.Abs(zoomLevel))*Math.Sign(zoomLevel);
238280
// "Physical" pixel render size
239281
Vector2 screenRenderSize = GetScreenRenderSize();
240282
// Pixel render size
241-
int[] pixelRenderSize = GetRenderTextureSize(screenRenderSize, aspect);
283+
int[] pixelRenderSize = GetRenderTextureSize(screenRenderSize, settings.aspect);
242284

243285
float targetAspect = (float)pixelRenderSize[0] / (float)pixelRenderSize[1];
244286
cam.aspect = targetAspect;
@@ -269,7 +311,9 @@ private void SetupCamera()
269311
renderTexture.Release();
270312

271313
// Create new render texture
272-
renderTexture = new RenderTexture(pixelRenderSize[0], pixelRenderSize[1], 0)
314+
Vector2 renderSize = new Vector2(pixelRenderSize[0], pixelRenderSize[1]) / settings.downsample;
315+
int[] actualRenderSize = GetRenderTextureSize(renderSize, Vector2.one);
316+
renderTexture = new RenderTexture(actualRenderSize[0], actualRenderSize[1], 0)
273317
{
274318
useMipMap = true,
275319
filterMode = FilterMode.Point,
@@ -282,13 +326,46 @@ private void SetupCamera()
282326
fallbackMaterial.SetTexture("_MainTex", renderTexture);
283327
if (advancedSettings != null)
284328
CameraMaterial = advancedSettings.cameraMaterial;
285-
286-
lastSettings = new CamSettings(aspect, zoomLevel, cam.fieldOfView, cam.orthographic);
329+
330+
lastSettings = settings;
287331

288332
cam.Render();
289333
camDraw.DrawQuad();
290334
}
291335

336+
private float GetPerspectiveHeight(float z)
337+
{
338+
var frustumHeight = 2.0f * z * Mathf.Tan(cam.fieldOfView * 0.5f * Mathf.Deg2Rad);
339+
return frustumHeight;
340+
}
341+
342+
protected Vector2 GetScreenRenderSize()
343+
{
344+
// For orthographic camera, physical render size is based on screen pixels
345+
Vector2 screenRenderSize = new Vector2(Screen.width, Screen.height);
346+
screenRenderSize /= zoomLevel;
347+
348+
// For perspective camera, physical render is based on world unit height
349+
// in terms of fustrum distance, converted to pixels
350+
if (cam.orthographic == false)
351+
{
352+
cam.aspect = (float) Screen.width / Screen.height;
353+
354+
float scale = Mathf.InverseLerp(cam.nearClipPlane, cam.farClipPlane, PerspectiveZ);
355+
float maxHeight = GetPerspectiveHeight(cam.farClipPlane);
356+
float minHeight = GetPerspectiveHeight(cam.nearClipPlane);
357+
358+
float height = Mathf.Lerp(minHeight, maxHeight, scale);
359+
float width = height*cam.aspect;
360+
361+
screenRenderSize.x = width;
362+
screenRenderSize.y = height;
363+
screenRenderSize *= pixelsPerUnit;
364+
}
365+
366+
return screenRenderSize;
367+
}
368+
292369
/// <summary>
293370
/// The integer width and height of the texture to render to
294371
/// </summary>
@@ -361,10 +438,10 @@ public void ForceRefresh()
361438

362439
public bool CheckCamera()
363440
{
364-
var currentSettings = new CamSettings(AspectStretch, zoomLevel, cam.fieldOfView, cam.orthographic);
441+
var currentSettings = new CamSettings(this, cam);
365442
bool didChange = currentSettings.Equals(lastSettings) == false;
366443
if (didChange)
367-
SetupCamera();
444+
SetupCamera(currentSettings);
368445
return didChange;
369446
}
370447
}

0 commit comments

Comments
 (0)