Howto get texture color from brep


we currently developing an application where we need to know, which texture color i have on a specific point on the surface.

i found some code snippets where the texture color is calculated from a colored mesh which is generated with the command ComputeVertexColors.

But i think it would be more straight forward to calculate the color directly from the texture and the corresponding texture mapping.
Do you have a code example where this is basically shown?

@andy, is this something you can help with?


Do you need an answer that is general for all Rhino models, or for a specific case?

The answer is basically that you need to look at the texture mapping (ON_MappingRef on m_rendering_attributes) and find the ON_TextureMapping. If there is no custom mapping, use the surface parameters to calculate the texture coordinate (see comments on ON_Mesh::m_S / m_T).

Once you have that, you need to find the material. And from there the texture in the diffuse channel. You will then be able to get a texture evaluator into which you can pass your UVW coordinates, and this will give you a color.

  • Andy

Hi Andy,

yes in theory everything is clear. My only gap is between the theory and the reality :wink:

Currently i tried something like that (at the moment it’s pretty hard coded and works only on my model):

CRhRdkObjectDataAccess da(obj);
const UUID uuidContent = da.MaterialInstanceId();
const CRhRdkContent* pContent = ::RhRdkFindContentInstance(uuidContent);
if (NULL != pContent && pContent->IsKind(RDK_KIND_MATERIAL))
	const CRhRdkMaterial* pMaterial = static_cast<const CRhRdkMaterial*>(pContent);

	CRhRdkContent::CChildIterator iter(pMaterial);
	CRhRdkContent* contTexture = iter.GetNextChild();
	if (contTexture && contTexture->IsKind(RDK_KIND_TEXTURE))
		const CRhRdkTexture* pTexture = static_cast<const CRhRdkTexture*>(contTexture);
		pTexEval = pTexture->NewTextureEvaluator();

		const CRhinoTextureMapping& tm = RhinoApp().ActiveDoc()->m_texture_mapping_table[0];
		ON_3dPoint T;
		int mapindex = tm.Evaluate(pointOnSurface, normalOnSurface, &T);

		CRhRdkColor col;
		pTexEval->GetColor(T, ON_3dVector(0, 0, 0), ON_3dVector(0, 0, 0), col);

It works pretty well except if i change the rotation in the texture mapping (see screenshot)

@andy is this the way you thought about and how can i solve the problem with the rotation?

This should get you a little closer.

static CRhRdkColor RdkObjectColor(const CRhinoObject& obj, const CRhRdkMaterial& material, const ON_3dPoint& uvw, const ON_3dPoint& xyz, const ON_3dVector& normal)
	CRhRdkContent::TextureChannelInfo diffuseInfo;	
	material.GetTextureChannelInfo(CRhRdkMaterial::ChildSlotUsage::kDiffuse, diffuseInfo);
	const CRhRdkTexture* pDiffuseTexture = dynamic_cast<const CRhRdkTexture*>(::RhRdkFindContentInstance(material.Document(), diffuseInfo.textureInstanceId));

	if (nullptr == pDiffuseTexture)
		//There's no texture - so return the diffuse color of the material.
		return diffuseInfo.color;

	//Now we need the texture mapping channel used by this texture - it's usually "1"
	const int iMappingChannel = pDiffuseTexture->MappingChannel();

	ON_TextureMapping mapping;
	ON_Xform xformLocal(1);

	const auto* pMappingRef = obj.Attributes().m_rendering_attributes.MappingRef(::RhRdkCurrentRenderEngineId());
	if (pMappingRef != nullptr && obj.Document() != nullptr)
		const auto* pMappingChannel = pMappingRef->MappingChannel(iMappingChannel);
		if (pMappingChannel != nullptr)
			const int iMappingIndex = obj.Document()->m_texture_mapping_table.FindTextureMapping(pMappingChannel->m_mapping_id);
			if (iMappingIndex != -1)
				mapping = obj.Document()->m_texture_mapping_table[iMappingIndex];
				xformLocal = pMappingChannel->m_object_xform;

	const ON_3dVector nullVector(0.0, 0.0, 0.0);
	ON_3dPoint pt = ON_origin;

	//Get the true UV from the mapping (for standard surface mapping, this will return the same as the input.
	if (mapping.m_type == ON_TextureMapping::TYPE::srfp_mapping)
		mapping.Evaluate(uvw, ON_3dVector(0.0, 0.0, 0.0), &pt);
		//Pre transform the inputs.
		const ON_3dPoint newXYZ = xyz * xformLocal;
		const ON_3dVector newNormal = normal * xformLocal;

		mapping.Evaluate(newXYZ, newNormal, &pt);

	//Now we have the correct UVW, we can get the color from the texture evaluator.
	auto eval = std::unique_ptr<IRhRdkTextureEvaluator>(pDiffuseTexture->NewTextureEvaluator());

	CRhRdkColor col = diffuseInfo.color;
	if (eval)
		eval->GetColor(pt, nullVector, nullVector, col);

	return col;

static CRhRdkColor ObjectColor(const CRhinoObject& obj, const ON_3dPoint& uvw, const ON_3dPoint& xyz, const ON_3dVector& normal)
	CRhRdkObjectDataAccess da(&obj);
	const UUID uuidContent = da.MaterialInstanceId();
	const CRhRdkMaterial* pMaterial = dynamic_cast<const CRhRdkMaterial*>(::RhRdkFindContentInstance(obj.Document(), uuidContent));
	if (nullptr == pMaterial)
		//This section is necessary in Rhino 5 - unfortunately.  Not in V6.
		//It creates an RDK material which provides texture evaluation for old-style materials.
		auto basic_material = std::unique_ptr<CRhRdkMaterial>(::RhRdkNewBasicMaterial(obj.ObjectMaterial()));
		return RdkObjectColor(obj, *basic_material, uvw, xyz, normal);

	return RdkObjectColor(obj, *pMaterial, uvw, xyz, normal);

That code explains a lot of the texturing in the RDK. But unfortunately this code does not run in my environment.
The class CRhRdkContent::TextureChannelInfo is missing and also the function ::RhRdkFindContentInstance has another declaration.

Is this code already for @serengeti or do i miss some SDK?

Anyway, i will try to adapt it and i think i manage to fix this code…

Thank you @andy

Ah yes - sorry, I didn’t have a V5 SDK handy. Just use the method you were using to get the child texture.

  • Andy

Sorry, but with your code i have the same issue.

If i change the UVW rotation in the mapping channel, i do not get the right result. If i change the UVW repeat it works perfect, also with the UVW offset.
@andy do you have any clue what is wrong?