vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b
This commit is contained in:
@@ -0,0 +1,412 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv {
|
||||
|
||||
TriangleRasterizeSettings::TriangleRasterizeSettings()
|
||||
{
|
||||
shadingType = RASTERIZE_SHADING_SHADED;
|
||||
cullingMode = RASTERIZE_CULLING_CW;
|
||||
glCompatibleMode = RASTERIZE_COMPAT_DISABLED;
|
||||
}
|
||||
|
||||
static void drawTriangle(Vec4f verts[3], Vec3f colors[3], Mat& depthBuf, Mat& colorBuf,
|
||||
TriangleRasterizeSettings settings)
|
||||
{
|
||||
// this will be useful during refactoring
|
||||
// if there's gonna be more supported data types
|
||||
CV_DbgAssert(depthBuf.empty() || depthBuf.type() == CV_32FC1);
|
||||
CV_DbgAssert(colorBuf.empty() || colorBuf.type() == CV_32FC3);
|
||||
|
||||
// any of buffers can be empty
|
||||
int width = std::max(colorBuf.cols, depthBuf.cols);
|
||||
int height = std::max(colorBuf.rows, depthBuf.rows);
|
||||
|
||||
Point minPt(width, height), maxPt(0, 0);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// round down to cover the whole pixel
|
||||
int x = (int)(verts[i][0]), y = (int)(verts[i][1]);
|
||||
minPt.x = std::min( x, minPt.x);
|
||||
minPt.y = std::min( y, minPt.y);
|
||||
maxPt.x = std::max(x + 1, maxPt.x);
|
||||
maxPt.y = std::max(y + 1, maxPt.y);
|
||||
}
|
||||
|
||||
minPt.x = std::max(minPt.x, 0); maxPt.x = std::min(maxPt.x, width);
|
||||
minPt.y = std::max(minPt.y, 0); maxPt.y = std::min(maxPt.y, height);
|
||||
|
||||
Point2f a(verts[0][0], verts[0][1]), b(verts[1][0], verts[1][1]), c(verts[2][0], verts[2][1]);
|
||||
Point2f bc = b - c, ac = a - c;
|
||||
float d = ac.x*bc.y - ac.y*bc.x;
|
||||
|
||||
// culling and degenerated triangle removal
|
||||
if ((settings.cullingMode == RASTERIZE_CULLING_CW && d <= 0) ||
|
||||
(settings.cullingMode == RASTERIZE_CULLING_CCW && d >= 0) ||
|
||||
(abs(d) < 1e-6))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float invd = 1.f / d;
|
||||
Vec3f zinv { verts[0][2], verts[1][2], verts[2][2] };
|
||||
Vec3f w { verts[0][3], verts[1][3], verts[2][3] };
|
||||
|
||||
for (int y = minPt.y; y < maxPt.y; y++)
|
||||
{
|
||||
for (int x = minPt.x; x < maxPt.x; x++)
|
||||
{
|
||||
Point2f p(x + 0.5f, y + 0.5f), pc = p - c;
|
||||
// barycentric coordinates
|
||||
Vec3f f;
|
||||
f[0] = ( pc.x * bc.y - pc.y * bc.x) * invd;
|
||||
f[1] = ( pc.y * ac.x - pc.x * ac.y) * invd;
|
||||
f[2] = 1.f - f[0] - f[1];
|
||||
// if inside the triangle
|
||||
if ((f[0] >= 0) && (f[1] >= 0) && (f[2] >= 0))
|
||||
{
|
||||
bool update = false;
|
||||
if (!depthBuf.empty())
|
||||
{
|
||||
float zCurrent = depthBuf.at<float>(height - 1 - y, x);
|
||||
float zNew = f[0] * zinv[0] + f[1] * zinv[1] + f[2] * zinv[2];
|
||||
if (zNew < zCurrent)
|
||||
{
|
||||
update = true;
|
||||
depthBuf.at<float>(height - 1 - y, x) = zNew;
|
||||
}
|
||||
}
|
||||
else // RASTERIZE_SHADING_WHITE
|
||||
{
|
||||
update = true;
|
||||
}
|
||||
|
||||
if (!colorBuf.empty() && update)
|
||||
{
|
||||
Vec3f color;
|
||||
if (settings.shadingType == RASTERIZE_SHADING_WHITE)
|
||||
{
|
||||
color = { 1.f, 1.f, 1.f };
|
||||
}
|
||||
else if (settings.shadingType == RASTERIZE_SHADING_FLAT)
|
||||
{
|
||||
color = colors[0];
|
||||
}
|
||||
else // TriangleShadingType::Shaded
|
||||
{
|
||||
float zInter = 1.0f / (f[0] * w[0] + f[1] * w[1] + f[2] * w[2]);
|
||||
color = { 0, 0, 0 };
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
color += (f[j] * w[j]) * colors[j];
|
||||
}
|
||||
color *= zInter;
|
||||
}
|
||||
colorBuf.at<Vec3f>(height - 1 - y, x) = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// values outside of [zNear, zFar] have to be restored
|
||||
// [0, 1] -> [zNear, zFar]
|
||||
static void linearizeDepth(const Mat& inbuf, const Mat& validMask, Mat outbuf, double zFar, double zNear)
|
||||
{
|
||||
CV_Assert(inbuf.type() == CV_32FC1);
|
||||
CV_Assert(validMask.type() == CV_8UC1 || validMask.type() == CV_8SC1 || validMask.type() == CV_BoolC1);
|
||||
CV_Assert(outbuf.type() == CV_32FC1);
|
||||
CV_Assert(outbuf.size() == inbuf.size());
|
||||
|
||||
float scaleNear = (float)(1.0 / zNear);
|
||||
float scaleFar = (float)(1.0 / zFar);
|
||||
for (int y = 0; y < inbuf.rows; y++)
|
||||
{
|
||||
const float* inp = inbuf.ptr<float>(y);
|
||||
const uchar * validPtr = validMask.ptr<uchar>(y);
|
||||
float * outp = outbuf.ptr<float>(y);
|
||||
for (int x = 0; x < inbuf.cols; x++)
|
||||
{
|
||||
if (validPtr[x])
|
||||
{
|
||||
float d = inp[x];
|
||||
// precision-optimized version of this:
|
||||
//float z = - zFar * zNear / (d * (zFar - zNear) - zFar);
|
||||
float z = 1.f / ((1.f - d) * scaleNear + d * scaleFar );
|
||||
outp[x] = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [zNear, zFar] -> [0, 1]
|
||||
static void invertDepth(const Mat& inbuf, Mat& outbuf, Mat& validMask, double zNear, double zFar)
|
||||
{
|
||||
CV_Assert(inbuf.type() == CV_32FC1);
|
||||
outbuf.create(inbuf.size(), CV_32FC1);
|
||||
validMask.create(inbuf.size(), CV_8UC1);
|
||||
|
||||
float fNear = (float)zNear, fFar = (float)zFar;
|
||||
float zadd = (float)(zFar / (zFar - zNear));
|
||||
float zmul = (float)(-zNear * zFar / (zFar - zNear));
|
||||
for (int y = 0; y < inbuf.rows; y++)
|
||||
{
|
||||
const float * inp = inbuf.ptr<float>(y);
|
||||
float * outp = outbuf.ptr<float>(y);
|
||||
uchar * validPtr = validMask.ptr<uchar>(y);
|
||||
for (int x = 0; x < inbuf.cols; x++)
|
||||
{
|
||||
float z = inp[x];
|
||||
uchar m = (z >= fNear) && (z <= fFar);
|
||||
z = std::max(std::min(z, fFar), fNear);
|
||||
// precision-optimized version of this:
|
||||
// outp[x] = (z - zNear) / z * zFar / (zFar - zNear);
|
||||
outp[x] = zadd + zmul / z;
|
||||
validPtr[x] = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void triangleRasterizeInternal(InputArray _vertices, InputArray _indices, InputArray _colors,
|
||||
Mat& colorBuf, Mat& depthBuf,
|
||||
InputArray world2cam, double fovyRadians, double zNear, double zFar,
|
||||
const TriangleRasterizeSettings& settings)
|
||||
{
|
||||
CV_Assert(world2cam.type() == CV_32FC1 || world2cam.type() == CV_64FC1);
|
||||
CV_Assert((world2cam.size() == Size {4, 3}) || (world2cam.size() == Size {4, 4}));
|
||||
|
||||
CV_Assert((fovyRadians > 0) && (fovyRadians < CV_PI));
|
||||
CV_Assert(zNear > 0);
|
||||
CV_Assert(zFar > zNear);
|
||||
|
||||
Mat cpMat;
|
||||
world2cam.getMat().convertTo(cpMat, CV_64FC1);
|
||||
Matx44d camPoseMat = Matx44d::eye();
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
camPoseMat(i, j) = cpMat.at<double>(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
if(_indices.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CV_CheckFalse(_vertices.empty(), "No vertices provided along with indices array");
|
||||
|
||||
Mat vertices, colors, triangles;
|
||||
int nVerts = 0, nColors = 0, nTriangles = 0;
|
||||
|
||||
int vertexType = _vertices.type();
|
||||
CV_Assert(vertexType == CV_32FC1 || vertexType == CV_32FC3);
|
||||
vertices = _vertices.getMat();
|
||||
// transform 3xN matrix to Nx3, except 3x3
|
||||
if ((_vertices.channels() == 1) && (_vertices.rows() == 3) && (_vertices.cols() != 3))
|
||||
{
|
||||
vertices = vertices.t();
|
||||
}
|
||||
// This transposition is performed on 1xN matrix so it's almost free in terms of performance
|
||||
vertices = vertices.reshape(3, 1).t();
|
||||
nVerts = (int)vertices.total();
|
||||
|
||||
int indexType = _indices.type();
|
||||
CV_Assert(indexType == CV_32SC1 || indexType == CV_32SC3);
|
||||
triangles = _indices.getMat();
|
||||
// transform 3xN matrix to Nx3, except 3x3
|
||||
if ((_indices.channels() == 1) && (_indices.rows() == 3) && (_indices.cols() != 3))
|
||||
{
|
||||
triangles = triangles.t();
|
||||
}
|
||||
// This transposition is performed on 1xN matrix so it's almost free in terms of performance
|
||||
triangles = triangles.reshape(3, 1).t();
|
||||
nTriangles = (int)triangles.total();
|
||||
|
||||
if (!_colors.empty())
|
||||
{
|
||||
int colorType = _colors.type();
|
||||
CV_Assert(colorType == CV_32FC1 || colorType == CV_32FC3);
|
||||
colors = _colors.getMat();
|
||||
// transform 3xN matrix to Nx3, except 3x3
|
||||
if ((_colors.channels() == 1) && (_colors.rows() == 3) && (_colors.cols() != 3))
|
||||
{
|
||||
colors = colors.t();
|
||||
}
|
||||
colors = colors.reshape(3, 1).t();
|
||||
nColors = (int)colors.total();
|
||||
|
||||
CV_Assert(nColors == nVerts);
|
||||
}
|
||||
|
||||
// any of buffers can be empty
|
||||
Size imgSize {std::max(colorBuf.cols, depthBuf.cols), std::max(colorBuf.rows, depthBuf.rows)};
|
||||
|
||||
// world-to-camera coord system
|
||||
Matx44d lookAtMatrix = camPoseMat;
|
||||
|
||||
double ys = 1.0 / std::tan(fovyRadians / 2);
|
||||
double xs = ys / (double)imgSize.width * (double)imgSize.height;
|
||||
double zz = (zNear + zFar) / (zNear - zFar);
|
||||
double zw = 2.0 * zFar * zNear / (zNear - zFar);
|
||||
|
||||
// camera to NDC: [-1, 1]^3
|
||||
Matx44d perspectMatrix (xs, 0, 0, 0,
|
||||
0, ys, 0, 0,
|
||||
0, 0, zz, zw,
|
||||
0, 0, -1, 0);
|
||||
|
||||
Matx44f mvpMatrix = perspectMatrix * lookAtMatrix;
|
||||
|
||||
// vertex transform stage
|
||||
|
||||
Mat screenVertices(vertices.size(), CV_32FC4);
|
||||
for (int i = 0; i < nVerts; i++)
|
||||
{
|
||||
Vec3f vglobal = vertices.at<Vec3f>(i);
|
||||
|
||||
float x_num = std::fma(mvpMatrix(0,0), vglobal[0],
|
||||
std::fma(mvpMatrix(0,1), vglobal[1],
|
||||
std::fma(mvpMatrix(0,2), vglobal[2], mvpMatrix(0,3))));
|
||||
float y_num = std::fma(mvpMatrix(1,0), vglobal[0],
|
||||
std::fma(mvpMatrix(1,1), vglobal[1],
|
||||
std::fma(mvpMatrix(1,2), vglobal[2], mvpMatrix(1,3))));
|
||||
float z_num = std::fma(mvpMatrix(2,0), vglobal[0],
|
||||
std::fma(mvpMatrix(2,1), vglobal[1],
|
||||
std::fma(mvpMatrix(2,2), vglobal[2], mvpMatrix(2,3))));
|
||||
float w_num = std::fma(mvpMatrix(3,0), vglobal[0],
|
||||
std::fma(mvpMatrix(3,1), vglobal[1],
|
||||
std::fma(mvpMatrix(3,2), vglobal[2], mvpMatrix(3,3))));
|
||||
|
||||
float invw = 1.f / w_num;
|
||||
|
||||
// [-1, 1]^3 => [0, width] x [0, height] x [0, 1]
|
||||
Vec4f vscreen = {
|
||||
std::fma(x_num * invw, 0.5f * (float)imgSize.width, 0.5f * (float)imgSize.width),
|
||||
std::fma(y_num * invw, 0.5f * (float)imgSize.height, 0.5f * (float)imgSize.height),
|
||||
std::fma(z_num * invw, 0.5f, 0.5f),
|
||||
invw
|
||||
};
|
||||
|
||||
screenVertices.at<Vec4f>(i) = vscreen;
|
||||
}
|
||||
|
||||
// draw stage
|
||||
|
||||
for (int t = 0; t < nTriangles; t++)
|
||||
{
|
||||
Vec3i tri = triangles.at<Vec3i>(t);
|
||||
|
||||
Vec3f col[3];
|
||||
Vec4f ver[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
int idx = tri[i];
|
||||
CV_DbgAssert(idx >= 0 && idx < nVerts);
|
||||
|
||||
col[i] = colors.empty() ? Vec3f::all(0) : colors.at<Vec3f>(idx);
|
||||
ver[i] = screenVertices.at<Vec4f>(idx);
|
||||
}
|
||||
|
||||
drawTriangle(ver, col, depthBuf, colorBuf, settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void triangleRasterizeDepth(InputArray _vertices, InputArray _indices, InputOutputArray _depthBuf,
|
||||
InputArray world2cam, double fovY, double zNear, double zFar,
|
||||
const TriangleRasterizeSettings& settings)
|
||||
{
|
||||
CV_Assert(!_depthBuf.empty());
|
||||
CV_Assert(_depthBuf.type() == CV_32FC1);
|
||||
|
||||
Mat emptyColorBuf;
|
||||
// out-of-range values from user-provided depthBuf should not be altered, let's mark them
|
||||
Mat_<uchar> validMask;
|
||||
Mat depthBuf;
|
||||
if (settings.glCompatibleMode == RASTERIZE_COMPAT_INVDEPTH)
|
||||
{
|
||||
depthBuf = _depthBuf.getMat();
|
||||
}
|
||||
else // RASTERIZE_COMPAT_DISABLED
|
||||
{
|
||||
invertDepth(_depthBuf.getMat(), depthBuf, validMask, zNear, zFar);
|
||||
}
|
||||
|
||||
triangleRasterizeInternal(_vertices, _indices, noArray(), emptyColorBuf, depthBuf, world2cam, fovY, zNear, zFar, settings);
|
||||
|
||||
if (settings.glCompatibleMode == RASTERIZE_COMPAT_DISABLED)
|
||||
{
|
||||
linearizeDepth(depthBuf, validMask, _depthBuf.getMat(), zFar, zNear);
|
||||
}
|
||||
}
|
||||
|
||||
void triangleRasterizeColor(InputArray _vertices, InputArray _indices, InputArray _colors, InputOutputArray _colorBuf,
|
||||
InputArray world2cam, double fovY, double zNear, double zFar,
|
||||
const TriangleRasterizeSettings& settings)
|
||||
{
|
||||
CV_Assert(!_colorBuf.empty());
|
||||
CV_Assert(_colorBuf.type() == CV_32FC3);
|
||||
Mat colorBuf = _colorBuf.getMat();
|
||||
|
||||
Mat depthBuf;
|
||||
if (_colors.empty())
|
||||
{
|
||||
// full white shading does not require depth test
|
||||
CV_Assert(settings.shadingType == RASTERIZE_SHADING_WHITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// internal depth buffer is not exposed outside
|
||||
depthBuf.create(_colorBuf.size(), CV_32FC1);
|
||||
depthBuf.setTo(1.0);
|
||||
}
|
||||
|
||||
triangleRasterizeInternal(_vertices, _indices, _colors, colorBuf, depthBuf, world2cam, fovY, zNear, zFar, settings);
|
||||
}
|
||||
|
||||
void triangleRasterize(InputArray _vertices, InputArray _indices, InputArray _colors,
|
||||
InputOutputArray _colorBuffer, InputOutputArray _depthBuffer,
|
||||
InputArray world2cam, double fovyRadians, double zNear, double zFar,
|
||||
const TriangleRasterizeSettings& settings)
|
||||
{
|
||||
if (_colors.empty())
|
||||
{
|
||||
CV_Assert(settings.shadingType == RASTERIZE_SHADING_WHITE);
|
||||
}
|
||||
|
||||
CV_Assert(!_colorBuffer.empty());
|
||||
CV_Assert(_colorBuffer.type() == CV_32FC3);
|
||||
CV_Assert(!_depthBuffer.empty());
|
||||
CV_Assert(_depthBuffer.type() == CV_32FC1);
|
||||
|
||||
CV_Assert(_depthBuffer.size() == _colorBuffer.size());
|
||||
|
||||
Mat colorBuf = _colorBuffer.getMat();
|
||||
|
||||
// out-of-range values from user-provided depthBuf should not be altered, let's mark them
|
||||
Mat_<uchar> validMask;
|
||||
Mat depthBuf;
|
||||
if (settings.glCompatibleMode == RASTERIZE_COMPAT_INVDEPTH)
|
||||
{
|
||||
depthBuf = _depthBuffer.getMat();
|
||||
}
|
||||
else // RASTERIZE_COMPAT_DISABLED
|
||||
{
|
||||
invertDepth(_depthBuffer.getMat(), depthBuf, validMask, zNear, zFar);
|
||||
}
|
||||
|
||||
triangleRasterizeInternal(_vertices, _indices, _colors, colorBuf, depthBuf, world2cam, fovyRadians, zNear, zFar, settings);
|
||||
|
||||
if (settings.glCompatibleMode == RASTERIZE_COMPAT_DISABLED)
|
||||
{
|
||||
linearizeDepth(depthBuf, validMask, _depthBuffer.getMat(), zFar, zNear);
|
||||
}
|
||||
}
|
||||
} // namespace cv
|
||||
Reference in New Issue
Block a user