I’m trying to implement supersampling antialiasing in my software renderer.And per my understanding,I need first render everything to a 4x- scaled buffer then average every four pixels’ colors of that 4x-scaled
buffer to a smaller 1X buffer.However,supersampling should be transparent to user,user would only pass in vertices in that 1X-scaled smaller buffer.And my question is how to map the vertices from a smaller buffer to a scaled buffer?
some definition of my buffers:
Surface sysBuffer;
Surface superSamplingSysBuffer;
static constexpr unsigned int ScreenWidth = 400;
static constexpr unsigned int ScreenHeight = 300;
static constexpr bool superSampling = true;
static constexpr unsigned int superSamplingNum = 4;
Initialization:
Graphics::Graphics(HWNDKey& key)
:
sysBuffer(ScreenWidth, ScreenHeight),
superSamplingSysBuffer(ScreenWidth* superSamplingNum, ScreenHeight* superSamplingNum)
Composition of a single frame:
void Game::ComposeFrame(){
gfx.DrawLineBresenham(0, 200, 200, 200, Color{ 255,255,255 });
}
Line Drawing:
void Graphics::DrawLineBresenham(float x1, float y1, float x2, float y2, Color c) {
if (Graphics::superSampling) {
x1 *= Graphics::superSamplingNum;//MY attempts to mapping points to bigger buffer
y1 *= Graphics::superSamplingNum;
x2 *= Graphics::superSamplingNum;
y2 *= Graphics::superSamplingNum;
}
if (fabs(x2 - x1) < 1e-6) {
if (y1 > y2)
{
std::swap(x1, x2);
std::swap(y1, y2);
}
int xInt1 = static_cast<int>((x1 + 0.5f));
for (; y1 < y2; y1++) {
PutPixel(xInt1, int(y1 + 0.5f), c);
}
return;
}
else {
float k, d;
k = (y2 - y1) / (x2 - x1);
if (k >= 1.f) {
if (y1 > y2)
{
std::swap(x1, x2);
std::swap(y1, y2);
}
d = 1.f - 0.5f * k;
for (; y1 < y2; y1++) {
PutPixel(int(x1 + 0.5f), int(y1 + 0.5f), c);
if (d >= 0.f)
{
x1++;
d += (1.f - k);
}
else {
d += 1.f;
}
}
return;
}
if (0.f <= k && k < 1.f) {
if (x1 > x2)
{
std::swap(x1, x2);
std::swap(y1, y2);
}
d = 0.5f - k;
for (; x1 < x2; x1++) {
PutPixel(int(x1 + 0.5f), int(y1 + 0.5f), c);
if (d < 0.f) {
y1++;
d += (1.f - k);
}
else {
d -= k;
}
}
return;
}
if (k >= -1.f && k < 0.f) {
if (x1 > x2) {
std::swap(x1, x2);
std::swap(y1, y2);
}
d = -0.5f - k;
for (; x1 < x2; x1++) {
PutPixel(int(x1 + 0.5f), int(y1 + 0.5f), c);
if (d > 0.f) {
y1--;
d -= (1.f + k);
}
else {
d -= k;
}
}
return;
}
if (k < -1.f) {
if (y1 < y2) {
std::swap(x1, x2);
std::swap(y1, y2);
}
d = -1.f - 0.5f * k;
for (; y1 > y2; y1--) {
PutPixel(int(x1 + 0.5f), int(y1 + 0.5f), c);
if (d < 0.f) {
x1++;
d -= (1.f + k);
}
else {
d -= 1.f;
}
}
return;
}
}
}
Finally downsampling:
void Graphics::EndFrame()
{
HRESULT hr;
if (superSampling) {//AVERAGING COLORS
float sum(4) = { 0 };
for (int i = 0; i < ScreenWidth; i++) {
for (int j = 0; j < ScreenHeight; j++) {
for (int k = i * superSamplingNum; k < (i * superSamplingNum + (superSamplingNum)); k++) {
for (int l = j * superSamplingNum; l < (j * superSamplingNum + (superSamplingNum )); l++) {
Color temp = superSamplingSysBuffer.GetPixel(k, j);
sum(0) += (float)temp.GetR();
sum(1) += (float)temp.GetG();
sum(2) += (float)temp.GetB();
sum(3) += (float)temp.GetA();
}
}
sum(0) *= 0.25f;
sum(1) *= 0.25f;
sum(2) *= 0.25f;
sum(3) *= 0.25f;
Color result((unsigned char)sum(3), (unsigned char)sum(0), (unsigned char)sum(1), (unsigned char)sum(2));
sysBuffer.PutPixel(i, j, result);
sum(0) = 0.f;
sum(1) = 0.f;
sum(2) = 0.f;
sum(3) = 0.f;
}
}
}
//SOME DX11 irrelevent things
// lock and map the adapter memory for copying over the sysbuffer
if (FAILED(hr = pImmediateContext->Map(pSysBufferTexture.Get(), 0u,
D3D11_MAP_WRITE_DISCARD, 0u, &mappedSysBufferTexture)))
{
throw CHILI_GFX_EXCEPTION(hr, L"Mapping sysbuffer");
}
// perform the copy line-by-line
sysBuffer.Present(mappedSysBufferTexture.RowPitch,
reinterpret_cast<BYTE*>(mappedSysBufferTexture.pData));
// release the adapter memory
pImmediateContext->Unmap(pSysBufferTexture.Get(), 0u);
// render offscreen scene texture to back buffer
pImmediateContext->IASetInputLayout(pInputLayout.Get());
pImmediateContext->VSSetShader(pVertexShader.Get(), nullptr, 0u);
pImmediateContext->PSSetShader(pPixelShader.Get(), nullptr, 0u);
pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
const UINT stride = sizeof(FSQVertex);
const UINT offset = 0u;
pImmediateContext->IASetVertexBuffers(0u, 1u, pVertexBuffer.GetAddressOf(), &stride, &offset);
pImmediateContext->PSSetShaderResources(0u, 1u, pSysBufferTextureView.GetAddressOf());
pImmediateContext->PSSetSamplers(0u, 1u, pSamplerState.GetAddressOf());
pImmediateContext->Draw(6u, 0u);
// flip back/front buffers
if (FAILED(hr = pSwapChain->Present(1u, 0u)))
{
throw CHILI_GFX_EXCEPTION(hr, L"Presenting back buffer");
}
}
Supersampling off:
Supersampling on:
Disired effect: