{"id":278,"date":"2015-08-06T15:28:07","date_gmt":"2015-08-06T19:28:07","guid":{"rendered":"https:\/\/blogs.mathworks.com\/graphics\/?p=278"},"modified":"2015-08-06T15:28:07","modified_gmt":"2015-08-06T19:28:07","slug":"transparency-in-3d","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/graphics\/2015\/08\/06\/transparency-in-3d\/","title":{"rendered":"Transparency in 3D"},"content":{"rendered":"<div class=\"content\"><h3>Transparency in 3D<\/h3><p>Transparency is a very useful feature when creating pictures in 3D. But there are a suprising number of things to think about when you're using transparency in 3D.<\/p><p>Let's look at a simple example to explore some of these issues.<\/p><p>First we'll create a patch with three rectangular faces.<\/p><pre class=\"codeinput\">u = [-1; 1; 1; -1];\r\nv = [-.67; -.67; .67; .67];\r\nw = [0; 0; 0; 0];\r\nh = patch([u,v,w],[v,w,u],[w,u,v],zeros(4,3), <span class=\"keyword\">...<\/span>\r\n          <span class=\"string\">'FaceVertexCData'<\/span>,eye(3),<span class=\"string\">'FaceColor'<\/span>,<span class=\"string\">'flat'<\/span>);\r\nview(3)\r\nbox <span class=\"string\">on<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/transparency3d_01.png\" alt=\"\"> <p>Next we'll make them partially transparent.<\/p><pre class=\"codeinput\">h.FaceAlpha = 1\/3;\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/transparency3d_02.png\" alt=\"\"> <p>Notice that there are two areas where we see just the blue and green patches.<\/p><pre class=\"codeinput\">a1 = annotation(<span class=\"string\">'textarrow'<\/span>,<span class=\"string\">'X'<\/span>,[.725 .6125],<span class=\"string\">'Y'<\/span>,[.05 .375],<span class=\"string\">'String'<\/span>,<span class=\"string\">'Green in back of Blue'<\/span>);\r\na2 = annotation(<span class=\"string\">'textarrow'<\/span>,<span class=\"string\">'X'<\/span>,[.365 .47],<span class=\"string\">'Y'<\/span>,[.93 .67],<span class=\"string\">'String'<\/span>,<span class=\"string\">'Green in front of Blue'<\/span>);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/transparency3d_03.png\" alt=\"\"> <p>But if we look closely, they're not the same color. The upper left area is greener, and the lower right area is bluer.<\/p><pre class=\"codeinput\">a1.String = <span class=\"string\">'Red=0.44, Green=0.67, Blue=0.78'<\/span>;\r\na2.String = <span class=\"string\">'Red=0.44, Green=0.78, Blue=0.67'<\/span>;\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/transparency3d_04.png\" alt=\"\"> <p>Why is that, and where do those numbers come from?<\/p><p>When we draw a transparent object, the color is determined by <a href=\"https:\/\/en.wikipedia.org\/wiki\/Alpha_compositing\">compositing<\/a> the object using a blend function that looks like this:<\/p><p>$$C_{after} = \\alpha*C_{object} + (1-\\alpha)*C_{before}$$<\/p><p>When we draw overlapping transparent objects, the result is the product of multiple compositing operations. The blending functions of the compositing operations multiply like this:<\/p><p>$$C_{after} = \\alpha_2*C_{object2} + (1-\\alpha_2)*(\\alpha_1*C_{object1} + (1-\\alpha_1)*C_{before})$$<\/p><p>If you stick our colors and alphas into those equations, you'll get the numbers we saw earlier.<\/p><pre class=\"codeinput\">a1.String = <span class=\"string\">'\\alpha Blue + (1-\\alpha)(\\alpha Green + (1-\\alpha)White)'<\/span>;\r\na2.String = <span class=\"string\">'\\alpha Green + (1-\\alpha)(\\alpha Blue + (1-\\alpha)White)'<\/span>;\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/transparency3d_05.png\" alt=\"\"> <p>The reason we have different colors in these two cases is that the blending function doesn't commute.<\/p><p>$$A \\otimes B \\neq B \\otimes A$$<\/p><p>There's another important point you should notice here. When we're drawing opaque objects (i.e. alpha=1), then the only thing that affects the final color of the pixel is the color of the last object to be drawn. But when we're drawing transparent objects (i.e. alpha&lt;1), then the final color of the pixel is affected by the colors of all of the objects that touch that pixel, and the order matters because the blending function doesn't commute. In the upper left, we needed to draw the blue patch before the green. But in the lower right, we needed to draw the green patch before the blue.<\/p><p>This means that depth sorting transparent objects is inherently more complex than depth sorting opaque objects. For the same reasons that MATLAB's <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/max.html\">max<\/a> function is simpler and faster than its <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/sort.html\">sort<\/a> function, the opaque depth sort is simpler and faster than the one we use for transparent objects.<\/p><p>The depth sort for opaque objects is a simple one called <a href=\"https:\/\/en.wikipedia.org\/wiki\/Z-buffering\">Z-buffering<\/a>. For depth sorting the transparent objects, there are a number of different choices. Each of them has its own strengths and weaknesses. In MATLAB Graphics, we do this depth sort with a technique called <a href=\"https:\/\/en.wikipedia.org\/wiki\/Order-independent_transparency\">Order Independent Transparency<\/a>, or OIT for short. We chose OIT because it works well on the current generation of graphics hardware, although there are still a few graphics cards which don't support it.<\/p><p>OIT's biggest weakness is that it struggles when it encounters objects with exactly the same depth. We can actually see that in this picture. Those faint lines where the patches intersect are caused by that.<\/p><p>The other thing you may have noticed is that when OIT kicks in, the antialiasing is lost. That's because OIT is implemented using the same resources as <a href=\"https:\/\/blogs.mathworks.com\/graphics\/2015\/07\/13\/graphicssmoothing-and-alignvertexcenters\/\">GraphicsSmoothing<\/a> and we can't currently do both at the same time.<\/p><p>In earlier versions of MATLAB we didn't use the graphics hardware to sort transparent objects. This meant that it didn't handle intersecting objects very well. Here's what R2014a did with this picture:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/R2014a_transparency3d.png\" alt=\"\"> <\/p><p>You can actually get a very similar effect with the new graphics system by setting the <a href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/11\/04\/sortmethod\/\">SortMethod<\/a>, although that will affect the opaque objects as well as the transparent ones. When SortMethod is set to childorder, then all of this gets a lot simpler. That's why you usually don't have to worry about these issues when you're drawing 2D scenes.<\/p><p>Now that we have a 3D scene with transparency, we might want to print it. There are a few more things to think about when we print 3D transparency.<\/p><p>By default, when you print we use a sorting technique which is more like the one that MATLAB used on screen before R2014b. This means that it isn't going to handle this intersecting patch case well.<\/p><pre class=\"language-matlab\">print <span class=\"string\">-dpdf<\/span> <span class=\"string\">transparency.pdf<\/span>\r\n<\/pre><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/oit_pdf_snapshot.PNG\" alt=\"\"> <\/p><p>Even worse, if you're printing to one of the PostScript file formats (.PS and .EPS), then you need to be aware of the fact that they do not support transparency at all! That's because colors in the PostScript language are all opaque and do not have alpha values.<\/p><pre class=\"language-matlab\">print <span class=\"string\">-dpsc<\/span> <span class=\"string\">transparency.ps<\/span>\r\n<\/pre><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/oit_ps_snapshot.PNG\" alt=\"\"> <\/p><p>That means that if you're using transparency, you'll either need to output to a file format which does support alpha (such as .PDF) or do the compositing before writing the results to the file.<\/p><p>The easiest way to do the compositing before writing the results to the file is to tell the print command to use OpenGL. When you do that, MATLAB will use OIT to generate an image with the correct sorting and compositing, and then save that image in the .PS file. But when you do this you might want to increase the resolution because the default is to use the screen resolution.<\/p><pre class=\"language-matlab\">print <span class=\"string\">-dpsc<\/span> <span class=\"string\">-opengl<\/span> <span class=\"string\">-r600<\/span> <span class=\"string\">transparency_opengl.ps<\/span>\r\n<\/pre><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/oit_opengl_snapshot.png\" alt=\"\"> <\/p><p>I hope that gave you some insight into what's happening when you use transparency in 3D with MATLAB Graphics.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_aefc0f47c44d4be0a32886953ad1ebfd() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='aefc0f47c44d4be0a32886953ad1ebfd ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' aefc0f47c44d4be0a32886953ad1ebfd';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2015 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_aefc0f47c44d4be0a32886953ad1ebfd()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2015a<br><\/p><\/div><!--\r\naefc0f47c44d4be0a32886953ad1ebfd ##### SOURCE BEGIN #####\r\n%% Transparency in 3D\r\n%\r\n% Transparency is a very useful feature when creating pictures in 3D. But\r\n% there are a suprising number of things to think about when you're using\r\n% transparency in 3D.\r\n%\r\n% Let's look at a simple example to explore some of these issues.\r\n%\r\n% First we'll create a patch with three rectangular faces.\r\n%\r\nu = [-1; 1; 1; -1];\r\nv = [-.67; -.67; .67; .67];\r\nw = [0; 0; 0; 0];\r\nh = patch([u,v,w],[v,w,u],[w,u,v],zeros(4,3), ...\r\n          'FaceVertexCData',eye(3),'FaceColor','flat');\r\nview(3)\r\nbox on\r\n%%\r\n% Next we'll make them partially transparent.\r\n%\r\nh.FaceAlpha = 1\/3;\r\n\r\n%%\r\n% Notice that there are two areas where we see just the blue and green\r\n% patches.\r\n%\r\na1 = annotation('textarrow','X',[.725 .6125],'Y',[.05 .375],'String','Green in back of Blue');\r\na2 = annotation('textarrow','X',[.365 .47],'Y',[.93 .67],'String','Green in front of Blue');\r\n\r\n%%\r\n% But if we look closely, they're not the same color. The upper left area \r\n% is greener, and the lower right area is bluer.\r\n%\r\na1.String = 'Red=0.44, Green=0.67, Blue=0.78';\r\na2.String = 'Red=0.44, Green=0.78, Blue=0.67';\r\n\r\n%%\r\n% Why is that, and where do those numbers come from?\r\n%\r\n% When we draw a transparent object, the color is determined by \r\n% <https:\/\/en.wikipedia.org\/wiki\/Alpha_compositing compositing>\r\n% the object using a blend function that looks like this:\r\n% \r\n% $$C_{after} = \\alpha*C_{object} + (1-\\alpha)*C_{before}$$\r\n%\r\n% When we draw overlapping transparent objects, the result is the product of\r\n% multiple compositing operations. The blending functions of the compositing \r\n% operations multiply like this:\r\n%\r\n% $$C_{after} = \\alpha_2*C_{object2} + (1-\\alpha_2)*(\\alpha_1*C_{object1} + (1-\\alpha_1)*C_{before})$$\r\n%\r\n% If you stick our colors and alphas into those equations, you'll get the numbers we saw\r\n% earlier.\r\n%\r\na1.String = '\\alpha Blue + (1-\\alpha)(\\alpha Green + (1-\\alpha)White)';\r\na2.String = '\\alpha Green + (1-\\alpha)(\\alpha Blue + (1-\\alpha)White)';\r\n\r\n%%\r\n% The reason we have different colors in these two cases is that the\r\n% blending function doesn't commute. \r\n%\r\n% $$A \\otimes B \\neq B \\otimes A$$\r\n%\r\n% There's another important point you should notice here. When we're drawing \r\n% opaque objects (i.e. alpha=1), then the only thing that affects the final\r\n% color of the pixel is the color of the last object to be drawn. But when\r\n% we're drawing transparent objects (i.e. alpha<1), then the final color of\r\n% the pixel is affected by the colors of all of the objects that touch that\r\n% pixel, and the order matters because the blending function doesn't\r\n% commute. In the upper left, we needed to draw the blue patch before\r\n% the green. But in the lower right, we needed to draw the green patch\r\n% before the blue.\r\n%\r\n% This means that depth sorting transparent objects is inherently more\r\n% complex than depth sorting opaque objects. For the same reasons that MATLAB's\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/max.html max>\r\n% function is simpler and faster than its <https:\/\/www.mathworks.com\/help\/matlab\/ref\/sort.html sort>\r\n% function, the opaque depth sort is simpler and faster than the one we use\r\n% for transparent objects.\r\n%\r\n% The depth sort for opaque objects is a simple one called\r\n% <https:\/\/en.wikipedia.org\/wiki\/Z-buffering Z-buffering>. For depth\r\n% sorting the transparent objects, there are a number of different choices.\r\n% Each of them has its own strengths and weaknesses. In\r\n% MATLAB Graphics, we do this depth sort with a technique called\r\n% <https:\/\/en.wikipedia.org\/wiki\/Order-independent_transparency Order\r\n% Independent Transparency>, or OIT for short. We chose OIT because it works \r\n% well on the current generation of graphics hardware, although there are \r\n% still a few graphics cards which don't support it. \r\n%\r\n% OIT's biggest weakness is that it struggles when it encounters objects \r\n% with exactly the same depth. We can actually see that in this picture. \r\n% Those faint lines where the patches intersect are caused by that.\r\n%\r\n% The other thing you may have noticed is that when OIT kicks in, the\r\n% antialiasing is lost. That's because OIT is implemented using the same\r\n% resources as\r\n% <https:\/\/blogs.mathworks.com\/graphics\/2015\/07\/13\/graphicssmoothing-and-alignvertexcenters\/\r\n% GraphicsSmoothing> and we can't currently do both at the same time.\r\n% \r\n% In earlier versions of MATLAB we didn't use the graphics hardware to sort \r\n% transparent objects. This meant that it didn't handle intersecting objects \r\n% very well. Here's what R2014a did with this picture:\r\n%\r\n% <<..\/R2014a.png>>\r\n%\r\n% You can actually get a very similar effect with the new graphics system by setting\r\n% the <https:\/\/blogs.mathworks.com\/graphics\/2014\/11\/04\/sortmethod\/ SortMethod>, \r\n% although that will affect the opaque objects as well as the transparent\r\n% ones. When SortMethod is set to childorder, then all of this gets a lot\r\n% simpler. That's why you usually don't have to worry about these issues\r\n% when you're drawing 2D scenes.\r\n%\r\n\r\n%%\r\n% Now that we have a 3D scene with transparency, we might want to print\r\n% it. There are a few more things to think about when we print 3D transparency.\r\n%\r\n% By default, when you print we use a sorting technique which is more like\r\n% the one that MATLAB used on screen before R2014b. This means that it\r\n% isn't going to handle this intersecting patch case well.\r\n%\r\n%   print -dpdf transparency.pdf\r\n%\r\n% <<..\/oit_pdf_snapshot.png>>\r\n%\r\n\r\n%%\r\n% Even worse, if you're printing to one of the PostScript file formats \r\n% (.PS and .EPS), then you need to be aware of the fact that they do not\r\n% support transparency at all! That's because colors in the PostScript \r\n% language are all opaque and do not have alpha values. \r\n%\r\n%   print -dpsc transparency.ps\r\n%\r\n% <<..\/oit_ps_snapshot.png>>\r\n%\r\n\r\n%%\r\n% That means that \r\n% if you're using transparency, you'll either need to output to a file \r\n% format which does support alpha (such as .PDF) or do the compositing \r\n% before writing the results to the file. \r\n\r\n%%\r\n% The easiest way to do the compositing before writing the results to the \r\n% file is to tell the print command to use OpenGL. When you do that, MATLAB \r\n% will use OIT to generate an image with the correct sorting and compositing,\r\n% and then save that image in the .PS file. But when you do this you might \r\n% want to increase the resolution because the default is to use the screen\r\n% resolution.\r\n%\r\n%   print -dpsc -opengl -r600 transparency_opengl.ps\r\n%\r\n% <<..\/oit_opengl_snapshot.png>>\r\n%\r\n% I hope that gave you some insight into what's happening when you use\r\n% transparency in 3D with MATLAB Graphics. \r\n%\r\n\r\n\r\n##### SOURCE END ##### aefc0f47c44d4be0a32886953ad1ebfd\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/graphics\/files\/feature_image\/oit_thumbnail.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>Transparency in 3DTransparency is a very useful feature when creating pictures in 3D. But there are a suprising number of things to think about when you're using transparency in 3D.Let's look at a... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/graphics\/2015\/08\/06\/transparency-in-3d\/\">read more >><\/a><\/p>","protected":false},"author":89,"featured_media":283,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[6],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/278"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/users\/89"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/comments?post=278"}],"version-history":[{"count":4,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/278\/revisions"}],"predecessor-version":[{"id":282,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/278\/revisions\/282"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media\/283"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media?parent=278"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/categories?post=278"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/tags?post=278"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}